1
Fork 0

make unevaluated const substs optional

This commit is contained in:
lcnr 2021-03-16 00:05:45 +01:00
parent f4b606fd17
commit bfaf13af4e
46 changed files with 234 additions and 188 deletions

View file

@ -38,7 +38,7 @@ impl<'tcx> TyCtxt<'tcx> {
ct: ty::Unevaluated<'tcx>,
span: Option<Span>,
) -> EvalToConstValueResult<'tcx> {
match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) {
match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs(self)) {
Ok(Some(instance)) => {
let cid = GlobalId { instance, promoted: ct.promoted };
self.const_eval_global_id(param_env, cid, span)

View file

@ -114,6 +114,10 @@ rustc_queries! {
desc { |tcx| "compute const default for a given parameter `{}`", tcx.def_path_str(param) }
}
query default_anon_const_substs(key: DefId) -> SubstsRef<'tcx> {
desc { |tcx| "computing the default generic arguments for `{}`", tcx.def_path_str(key) }
}
/// Records the type of every item.
query type_of(key: DefId) -> Ty<'tcx> {
desc { |tcx| "computing type of `{}`", tcx.def_path_str(key) }

View file

@ -1,6 +1,5 @@
use crate::mir::interpret::ConstValue;
use crate::mir::interpret::{LitToConstInput, Scalar};
use crate::ty::subst::InternalSubsts;
use crate::ty::{self, Ty, TyCtxt};
use crate::ty::{ParamEnv, ParamEnvAnd};
use rustc_errors::ErrorReported;
@ -100,7 +99,7 @@ impl<'tcx> Const<'tcx> {
}
_ => ty::ConstKind::Unevaluated(ty::Unevaluated {
def: def.to_global(),
substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()),
substs_: None,
promoted: None,
}),
};

View file

@ -16,10 +16,23 @@ use super::ScalarInt;
#[derive(Hash, HashStable)]
pub struct Unevaluated<'tcx> {
pub def: ty::WithOptConstParam<DefId>,
pub substs: SubstsRef<'tcx>,
pub substs_: Option<SubstsRef<'tcx>>,
pub promoted: Option<Promoted>,
}
impl<'tcx> Unevaluated<'tcx> {
pub fn new(def: ty::WithOptConstParam<DefId>, substs: SubstsRef<'tcx>) -> Unevaluated<'tcx> {
Unevaluated { def, substs_: Some(substs), promoted: None }
}
pub fn substs(self, tcx: TyCtxt<'tcx>) -> SubstsRef<'tcx> {
self.substs_.unwrap_or_else(|| {
debug_assert_eq!(self.promoted, None);
tcx.default_anon_const_substs(self.def.did)
})
}
}
/// Represents a constant in Rust.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
#[derive(Hash, HashStable)]
@ -109,7 +122,7 @@ impl<'tcx> ConstKind<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
) -> Option<Result<ConstValue<'tcx>, ErrorReported>> {
if let ConstKind::Unevaluated(Unevaluated { def, substs, promoted }) = self {
if let ConstKind::Unevaluated(unevaluated) = self {
use crate::mir::interpret::ErrorHandled;
// HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
@ -118,29 +131,32 @@ impl<'tcx> ConstKind<'tcx> {
// Note that we erase regions *before* calling `with_reveal_all_normalized`,
// so that we don't try to invoke this query with
// any region variables.
let param_env_and_substs = tcx
let param_env_and = tcx
.erase_regions(param_env)
.with_reveal_all_normalized(tcx)
.and(tcx.erase_regions(substs));
.and(tcx.erase_regions(unevaluated));
// HACK(eddyb) when the query key would contain inference variables,
// attempt using identity substs and `ParamEnv` instead, that will succeed
// when the expression doesn't depend on any parameters.
// FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
// we can call `infcx.const_eval_resolve` which handles inference variables.
let param_env_and_substs = if param_env_and_substs.needs_infer() {
tcx.param_env(def.did).and(InternalSubsts::identity_for_item(tcx, def.did))
let param_env_and = if param_env_and.needs_infer() {
tcx.param_env(unevaluated.def.did).and(ty::Unevaluated {
def: unevaluated.def,
substs_: Some(InternalSubsts::identity_for_item(tcx, unevaluated.def.did)),
promoted: unevaluated.promoted,
})
} else {
param_env_and_substs
param_env_and
};
// FIXME(eddyb) maybe the `const_eval_*` methods should take
// `ty::ParamEnvAnd<SubstsRef>` instead of having them separate.
let (param_env, substs) = param_env_and_substs.into_parts();
// `ty::ParamEnvAnd` instead of having them separate.
let (param_env, unevaluated) = param_env_and.into_parts();
// try to resolve e.g. associated constants to their definition on an impl, and then
// evaluate the const.
match tcx.const_eval_resolve(param_env, ty::Unevaluated { def, substs, promoted }, None)
{
match tcx.const_eval_resolve(param_env, unevaluated, None) {
// NOTE(eddyb) `val` contains no lifetimes/types/consts,
// and we use the original type, so nothing from `substs`
// (which may be identity substs, see above),

View file

@ -305,7 +305,8 @@ impl FlagComputation {
}
fn add_unevaluated_const(&mut self, ct: ty::Unevaluated<'tcx>) {
self.add_substs(ct.substs);
// TODO
self.add_substs(ct.substs_.unwrap());
self.add_flags(TypeFlags::HAS_CT_PROJECTION);
}

View file

@ -192,9 +192,11 @@ pub trait TypeVisitor<'tcx>: Sized {
/// Supplies the `tcx` for an unevaluated anonymous constant in case its default substs
/// are not yet supplied.
///
/// Visitors which do not look into these substs may leave this unimplemented, so be
/// careful when calling this method elsewhere.
fn tcx_for_anon_const_substs<'a>(&'a self) -> TyCtxt<'tcx>;
/// Visitors which do not look into these substs may return `None` here, in which case
/// `super_visit_with` completely skips the default substs. Incorrectly returning
/// `None` can very quickly lead to ICE or other critical bugs, so be careful and
/// try to return an actual `tcx` if at all possible.
fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>>;
fn visit_binder<T: TypeFoldable<'tcx>>(
&mut self,
@ -336,8 +338,8 @@ impl<'tcx> TyCtxt<'tcx> {
{
type BreakTy = ();
fn tcx_for_anon_const_substs(&self) -> TyCtxt<'tcx> {
self.tcx
fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
Some(self.tcx)
}
fn visit_binder<T: TypeFoldable<'tcx>>(
@ -788,8 +790,9 @@ impl<'tcx> ValidateBoundVars<'tcx> {
impl<'tcx> TypeVisitor<'tcx> for ValidateBoundVars<'tcx> {
type BreakTy = ();
fn tcx_for_anon_const_substs(&self) -> TyCtxt<'tcx> {
bug!("default anon const substs can't contain bound vars");
fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
// Anonymous constants do not contain bound vars in their substs by default.
None
}
fn visit_binder<T: TypeFoldable<'tcx>>(
@ -1006,8 +1009,9 @@ struct HasEscapingVarsVisitor {
impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor {
type BreakTy = FoundEscapingVars;
fn tcx_for_anon_const_substs(&self) -> TyCtxt<'tcx> {
bug!("tcx_for_anon_const_substs called for HasEscpaingVarsVisitor");
fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
// Anonymous constants do not contain bound vars in their substs by default.
None
}
fn visit_binder<T: TypeFoldable<'tcx>>(
@ -1080,8 +1084,13 @@ struct HasTypeFlagsVisitor {
impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
type BreakTy = FoundFlags;
fn tcx_for_anon_const_substs(&self) -> TyCtxt<'tcx> {
bug!("tcx_for_anon_const_substs called for HasTypeFlagsVisitor");
fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
// TypeFlagsVisitor must not look into the default anon const substs
// as that would cause cycle errors, but we do care about them for
// some flags.
//
// We therefore have to be very careful here.
None
}
#[inline]
@ -1164,8 +1173,8 @@ impl LateBoundRegionsCollector<'tcx> {
}
impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector<'tcx> {
fn tcx_for_anon_const_substs(&self) -> TyCtxt<'tcx> {
self.tcx
fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
Some(self.tcx)
}
fn visit_binder<T: TypeFoldable<'tcx>>(

View file

@ -927,29 +927,28 @@ pub trait PrettyPrinter<'tcx>:
}
match ct.val {
ty::ConstKind::Unevaluated(ty::Unevaluated {
def,
substs,
promoted: Some(promoted),
}) => {
p!(print_value_path(def.did, substs));
p!(write("::{:?}", promoted));
}
ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted: None }) => {
match self.tcx().def_kind(def.did) {
DefKind::Static | DefKind::Const | DefKind::AssocConst => {
p!(print_value_path(def.did, substs))
}
_ => {
if def.is_local() {
let span = self.tcx().def_span(def.did);
if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) {
p!(write("{}", snip))
ty::ConstKind::Unevaluated(uv) => {
if let Some(promoted) = uv.promoted {
let substs = uv.substs_.unwrap();
p!(print_value_path(uv.def.did, substs));
p!(write("::{:?}", promoted));
} else {
let tcx = self.tcx();
match tcx.def_kind(uv.def.did) {
DefKind::Static | DefKind::Const | DefKind::AssocConst => {
p!(print_value_path(uv.def.did, uv.substs(tcx)))
}
_ => {
if uv.def.is_local() {
let span = tcx.def_span(uv.def.did);
if let Ok(snip) = tcx.sess.source_map().span_to_snippet(span) {
p!(write("{}", snip))
} else {
print_underscore!()
}
} else {
print_underscore!()
}
} else {
print_underscore!()
}
}
}
@ -2025,8 +2024,8 @@ impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_, 'tcx> {
type BreakTy = ();
fn tcx_for_anon_const_substs(&self) -> TyCtxt<'tcx> {
self.tcx
fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
Some(self.tcx)
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {

View file

@ -579,7 +579,7 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
(ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu))
if tcx.features().const_evaluatable_checked =>
{
tcx.try_unify_abstract_consts(((au.def, au.substs), (bu.def, bu.substs)))
tcx.try_unify_abstract_consts(((au.def, au.substs(tcx)), (bu.def, bu.substs(tcx))))
}
// While this is slightly incorrect, it shouldn't matter for `min_const_generics`
@ -591,13 +591,13 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
let substs = relation.relate_with_variance(
ty::Variance::Invariant,
ty::VarianceDiagInfo::default(),
au.substs,
bu.substs,
au.substs(tcx),
bu.substs(tcx),
)?;
return Ok(tcx.mk_const(ty::Const {
val: ty::ConstKind::Unevaluated(ty::Unevaluated {
def: au.def,
substs,
substs_: Some(substs),
promoted: au.promoted,
}),
ty: a.ty,

View file

@ -1046,13 +1046,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> {
match self {
ty::ConstKind::Infer(ic) => ty::ConstKind::Infer(ic.fold_with(folder)),
ty::ConstKind::Param(p) => ty::ConstKind::Param(p.fold_with(folder)),
ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) => {
ty::ConstKind::Unevaluated(ty::Unevaluated {
def,
substs: substs.fold_with(folder),
promoted,
})
}
ty::ConstKind::Unevaluated(uv) => ty::ConstKind::Unevaluated(uv.fold_with(folder)),
ty::ConstKind::Value(_)
| ty::ConstKind::Bound(..)
| ty::ConstKind::Placeholder(..)
@ -1064,7 +1058,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> {
match *self {
ty::ConstKind::Infer(ic) => ic.visit_with(visitor),
ty::ConstKind::Param(p) => p.visit_with(visitor),
ty::ConstKind::Unevaluated(ct) => ct.substs.visit_with(visitor),
ty::ConstKind::Unevaluated(uv) => uv.visit_with(visitor),
ty::ConstKind::Value(_)
| ty::ConstKind::Bound(..)
| ty::ConstKind::Placeholder(_)
@ -1082,3 +1076,24 @@ impl<'tcx> TypeFoldable<'tcx> for InferConst<'tcx> {
ControlFlow::CONTINUE
}
}
impl<'tcx> TypeFoldable<'tcx> for ty::Unevaluated<'tcx> {
fn super_fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self {
ty::Unevaluated {
def: self.def,
substs_: Some(self.substs(folder.tcx()).fold_with(folder)),
promoted: self.promoted,
}
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
if let Some(tcx) = visitor.tcx_for_anon_const_substs() {
self.substs(tcx).visit_with(visitor)
} else if let Some(substs) = self.substs_ {
substs.visit_with(visitor)
} else {
debug!("ignoring default substs of `{:?}`", self.def);
ControlFlow::CONTINUE
}
}
}

View file

@ -196,7 +196,8 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
| ty::ConstKind::Error(_) => {}
ty::ConstKind::Unevaluated(ct) => {
stack.extend(ct.substs.iter().rev());
// TODO
stack.extend(ct.substs_.unwrap().iter().rev());
}
}
}