mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Auto merge of #99798 - JulianKnodt:ac1, r=BoxyUwU
Add `ConstKind::Expr` Starting to implement `ty::ConstKind::Abstract`, most of the match cases are stubbed out, some I was unsure what to add, others I didn't want to add until a more complete implementation was ready. r? `@lcnr`
This commit is contained in:
commit
aff003becd
@ -561,6 +561,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => {
|
||||
throw_inval!(TooGeneric)
|
||||
}
|
||||
// FIXME(generic_const_exprs): `ConstKind::Expr` should be able to be evaluated
|
||||
ty::ConstKind::Expr(_) => throw_inval!(TooGeneric),
|
||||
ty::ConstKind::Error(reported) => {
|
||||
throw_inval!(AlreadyReported(reported))
|
||||
}
|
||||
|
@ -248,6 +248,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
|
||||
ty::ConstKind::Param(_)
|
||||
| ty::ConstKind::Value(_)
|
||||
| ty::ConstKind::Unevaluated(..)
|
||||
| ty::ConstKind::Expr(..)
|
||||
| ty::ConstKind::Error(_) => ct.super_fold_with(self),
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::traits::select;
|
||||
use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind};
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::fold::BoundVarReplacerDelegate;
|
||||
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
|
||||
@ -713,32 +712,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
TypeErrCtxt { infcx: self, typeck_results: None, fallback_has_occurred: false }
|
||||
}
|
||||
|
||||
/// calls `tcx.try_unify_abstract_consts` after
|
||||
/// canonicalizing the consts.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub fn try_unify_abstract_consts(
|
||||
&self,
|
||||
a: ty::UnevaluatedConst<'tcx>,
|
||||
b: ty::UnevaluatedConst<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> bool {
|
||||
// Reject any attempt to unify two unevaluated constants that contain inference
|
||||
// variables, since inference variables in queries lead to ICEs.
|
||||
if a.substs.has_non_region_infer()
|
||||
|| b.substs.has_non_region_infer()
|
||||
|| param_env.has_non_region_infer()
|
||||
{
|
||||
debug!("a or b or param_env contain infer vars in its substs -> cannot unify");
|
||||
return false;
|
||||
}
|
||||
|
||||
let param_env_and = param_env.and((a, b));
|
||||
let erased = self.tcx.erase_regions(param_env_and);
|
||||
debug!("after erase_regions: {:?}", erased);
|
||||
|
||||
self.tcx.try_unify_abstract_consts(erased)
|
||||
}
|
||||
|
||||
pub fn is_in_snapshot(&self) -> bool {
|
||||
self.in_snapshot.get()
|
||||
}
|
||||
@ -1646,26 +1619,25 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
|
||||
// Postpone the evaluation of constants whose substs depend on inference
|
||||
// variables
|
||||
let tcx = self.tcx;
|
||||
if substs.has_non_region_infer() {
|
||||
let ac = AbstractConst::new(self.tcx, unevaluated);
|
||||
match ac {
|
||||
Ok(None) => {
|
||||
substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did);
|
||||
param_env = self.tcx.param_env(unevaluated.def.did);
|
||||
if let Some(ct) = tcx.bound_abstract_const(unevaluated.def)? {
|
||||
let ct = tcx.expand_abstract_consts(ct.subst(tcx, substs));
|
||||
if let Err(e) = ct.error_reported() {
|
||||
return Err(ErrorHandled::Reported(e));
|
||||
} else if ct.has_non_region_infer() || ct.has_non_region_param() {
|
||||
return Err(ErrorHandled::TooGeneric);
|
||||
} else {
|
||||
substs = replace_param_and_infer_substs_with_placeholder(tcx, substs);
|
||||
}
|
||||
Ok(Some(ct)) => {
|
||||
if ct.unify_failure_kind(self.tcx) == FailureKind::Concrete {
|
||||
substs = replace_param_and_infer_substs_with_placeholder(self.tcx, substs);
|
||||
} else {
|
||||
return Err(ErrorHandled::TooGeneric);
|
||||
}
|
||||
}
|
||||
Err(guar) => return Err(ErrorHandled::Reported(guar)),
|
||||
} else {
|
||||
substs = InternalSubsts::identity_for_item(tcx, unevaluated.def.did);
|
||||
param_env = tcx.param_env(unevaluated.def.did);
|
||||
}
|
||||
}
|
||||
|
||||
let param_env_erased = self.tcx.erase_regions(param_env);
|
||||
let substs_erased = self.tcx.erase_regions(substs);
|
||||
let param_env_erased = tcx.erase_regions(param_env);
|
||||
let substs_erased = tcx.erase_regions(substs);
|
||||
debug!(?param_env_erased);
|
||||
debug!(?substs_erased);
|
||||
|
||||
@ -1673,7 +1645,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
|
||||
// The return value is the evaluated value which doesn't contain any reference to inference
|
||||
// variables, thus we don't need to substitute back the original values.
|
||||
self.tcx.const_eval_resolve_for_typeck(param_env_erased, unevaluated, span)
|
||||
tcx.const_eval_resolve_for_typeck(param_env_erased, unevaluated, span)
|
||||
}
|
||||
|
||||
/// `ty_or_const_infer_var_changed` is equivalent to one of these two:
|
||||
|
@ -644,12 +644,6 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Symbol {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [ty::abstract_const::Node<'tcx>] {
|
||||
fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self {
|
||||
ty::codec::RefDecodable::decode(d)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] {
|
||||
fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self {
|
||||
ty::codec::RefDecodable::decode(d)
|
||||
|
@ -366,7 +366,7 @@ define_tables! {
|
||||
mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>,
|
||||
promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>,
|
||||
// FIXME(compiler-errors): Why isn't this a LazyArray?
|
||||
thir_abstract_const: Table<DefIndex, LazyValue<&'static [ty::abstract_const::Node<'static>]>>,
|
||||
thir_abstract_const: Table<DefIndex, LazyValue<ty::Const<'static>>>,
|
||||
impl_parent: Table<DefIndex, RawDefId>,
|
||||
impl_polarity: Table<DefIndex, ty::ImplPolarity>,
|
||||
constness: Table<DefIndex, hir::Constness>,
|
||||
|
@ -476,6 +476,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
|
||||
// These variants shouldn't exist in the MIR.
|
||||
ty::ConstKind::Placeholder(_)
|
||||
| ty::ConstKind::Infer(_)
|
||||
| ty::ConstKind::Expr(_)
|
||||
| ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", literal),
|
||||
},
|
||||
ConstantKind::Unevaluated(uv, _) => {
|
||||
|
@ -1185,7 +1185,8 @@ pub enum NullOp {
|
||||
AlignOf,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
|
||||
pub enum UnOp {
|
||||
/// The `!` operator for logical inversion
|
||||
Not,
|
||||
@ -1193,7 +1194,8 @@ pub enum UnOp {
|
||||
Neg,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
|
||||
#[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub enum BinOp {
|
||||
/// The `+` operator (addition)
|
||||
Add,
|
||||
|
@ -16,9 +16,7 @@ TrivialTypeTraversalAndLiftImpls! {
|
||||
UserTypeAnnotationIndex,
|
||||
BorrowKind,
|
||||
CastKind,
|
||||
BinOp,
|
||||
NullOp,
|
||||
UnOp,
|
||||
hir::Movability,
|
||||
BasicBlock,
|
||||
SwitchTargets,
|
||||
|
@ -400,7 +400,7 @@ rustc_queries! {
|
||||
/// Try to build an abstract representation of the given constant.
|
||||
query thir_abstract_const(
|
||||
key: DefId
|
||||
) -> Result<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> {
|
||||
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
|
||||
desc {
|
||||
|tcx| "building an abstract representation for `{}`", tcx.def_path_str(key),
|
||||
}
|
||||
@ -409,7 +409,7 @@ rustc_queries! {
|
||||
/// Try to build an abstract representation of the given constant.
|
||||
query thir_abstract_const_of_const_arg(
|
||||
key: (LocalDefId, DefId)
|
||||
) -> Result<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> {
|
||||
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
|
||||
desc {
|
||||
|tcx|
|
||||
"building an abstract representation for the const argument `{}`",
|
||||
@ -417,15 +417,6 @@ rustc_queries! {
|
||||
}
|
||||
}
|
||||
|
||||
query try_unify_abstract_consts(key:
|
||||
ty::ParamEnvAnd<'tcx, (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>
|
||||
)>) -> bool {
|
||||
desc {
|
||||
|tcx| "trying to unify the generic constants `{}` and `{}`",
|
||||
tcx.def_path_str(key.value.0.def.did), tcx.def_path_str(key.value.1.def.did)
|
||||
}
|
||||
}
|
||||
|
||||
query mir_drops_elaborated_and_const_checked(
|
||||
key: ty::WithOptConstParam<LocalDefId>
|
||||
) -> &'tcx Steal<mir::Body<'tcx>> {
|
||||
|
@ -1,98 +1,13 @@
|
||||
//! A subset of a mir body used for const evaluatability checking.
|
||||
use crate::mir;
|
||||
use crate::ty::visit::TypeVisitable;
|
||||
use crate::ty::{self, EarlyBinder, SubstsRef, Ty, TyCtxt};
|
||||
use crate::ty::{
|
||||
self, Const, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
|
||||
TypeVisitable,
|
||||
};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use std::cmp;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// An index into an `AbstractConst`.
|
||||
pub struct NodeId {
|
||||
derive [HashStable]
|
||||
DEBUG_FORMAT = "n{}",
|
||||
}
|
||||
}
|
||||
|
||||
/// A tree representing an anonymous constant.
|
||||
///
|
||||
/// This is only able to represent a subset of `MIR`,
|
||||
/// and should not leak any information about desugarings.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct AbstractConst<'tcx> {
|
||||
// FIXME: Consider adding something like `IndexSlice`
|
||||
// and use this here.
|
||||
inner: &'tcx [Node<'tcx>],
|
||||
substs: SubstsRef<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> AbstractConst<'tcx> {
|
||||
pub fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
uv: ty::UnevaluatedConst<'tcx>,
|
||||
) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
|
||||
let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?;
|
||||
debug!("AbstractConst::new({:?}) = {:?}", uv, inner);
|
||||
Ok(inner.map(|inner| AbstractConst { inner, substs: tcx.erase_regions(uv.substs) }))
|
||||
}
|
||||
|
||||
pub fn from_const(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ct: ty::Const<'tcx>,
|
||||
) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
|
||||
match ct.kind() {
|
||||
ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv),
|
||||
ty::ConstKind::Error(reported) => Err(reported),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> {
|
||||
AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> {
|
||||
let node = self.inner.last().copied().unwrap();
|
||||
match node {
|
||||
Node::Leaf(leaf) => Node::Leaf(EarlyBinder(leaf).subst(tcx, self.substs)),
|
||||
Node::Cast(kind, operand, ty) => {
|
||||
Node::Cast(kind, operand, EarlyBinder(ty).subst(tcx, self.substs))
|
||||
}
|
||||
// Don't perform substitution on the following as they can't directly contain generic params
|
||||
Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unify_failure_kind(self, tcx: TyCtxt<'tcx>) -> FailureKind {
|
||||
let mut failure_kind = FailureKind::Concrete;
|
||||
walk_abstract_const::<!, _>(tcx, self, |node| {
|
||||
match node.root(tcx) {
|
||||
Node::Leaf(leaf) => {
|
||||
if leaf.has_non_region_infer() {
|
||||
failure_kind = FailureKind::MentionsInfer;
|
||||
} else if leaf.has_non_region_param() {
|
||||
failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
|
||||
}
|
||||
}
|
||||
Node::Cast(_, _, ty) => {
|
||||
if ty.has_non_region_infer() {
|
||||
failure_kind = FailureKind::MentionsInfer;
|
||||
} else if ty.has_non_region_param() {
|
||||
failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
|
||||
}
|
||||
}
|
||||
Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {}
|
||||
}
|
||||
ControlFlow::CONTINUE
|
||||
});
|
||||
failure_kind
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
|
||||
#[derive(Hash, Debug, Clone, Copy, Ord, PartialOrd, PartialEq, Eq)]
|
||||
#[derive(TyDecodable, TyEncodable, HashStable, TypeVisitable, TypeFoldable)]
|
||||
pub enum CastKind {
|
||||
/// thir::ExprKind::As
|
||||
As,
|
||||
@ -100,16 +15,6 @@ pub enum CastKind {
|
||||
Use,
|
||||
}
|
||||
|
||||
/// A node of an `AbstractConst`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
|
||||
pub enum Node<'tcx> {
|
||||
Leaf(ty::Const<'tcx>),
|
||||
Binop(mir::BinOp, NodeId, NodeId),
|
||||
UnaryOp(mir::UnOp, NodeId),
|
||||
FunctionCall(NodeId, &'tcx [NodeId]),
|
||||
Cast(CastKind, NodeId, Ty<'tcx>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
|
||||
pub enum NotConstEvaluatable {
|
||||
Error(ErrorGuaranteed),
|
||||
@ -127,68 +32,53 @@ TrivialTypeTraversalAndLiftImpls! {
|
||||
NotConstEvaluatable,
|
||||
}
|
||||
|
||||
pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>, ErrorGuaranteed>;
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
#[inline]
|
||||
pub fn thir_abstract_const_opt_const_arg(
|
||||
/// Returns a const without substs applied
|
||||
pub fn bound_abstract_const(
|
||||
self,
|
||||
def: ty::WithOptConstParam<DefId>,
|
||||
) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
|
||||
if let Some((did, param_did)) = def.as_const_arg() {
|
||||
uv: ty::WithOptConstParam<DefId>,
|
||||
) -> BoundAbstractConst<'tcx> {
|
||||
let ac = if let Some((did, param_did)) = uv.as_const_arg() {
|
||||
self.thir_abstract_const_of_const_arg((did, param_did))
|
||||
} else {
|
||||
self.thir_abstract_const(def.did)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx, f), level = "debug")]
|
||||
pub fn walk_abstract_const<'tcx, R, F>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ct: AbstractConst<'tcx>,
|
||||
mut f: F,
|
||||
) -> ControlFlow<R>
|
||||
where
|
||||
F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
|
||||
{
|
||||
#[instrument(skip(tcx, f), level = "debug")]
|
||||
fn recurse<'tcx, R>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ct: AbstractConst<'tcx>,
|
||||
f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
|
||||
) -> ControlFlow<R> {
|
||||
f(ct)?;
|
||||
let root = ct.root(tcx);
|
||||
debug!(?root);
|
||||
match root {
|
||||
Node::Leaf(_) => ControlFlow::CONTINUE,
|
||||
Node::Binop(_, l, r) => {
|
||||
recurse(tcx, ct.subtree(l), f)?;
|
||||
recurse(tcx, ct.subtree(r), f)
|
||||
}
|
||||
Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f),
|
||||
Node::FunctionCall(func, args) => {
|
||||
recurse(tcx, ct.subtree(func), f)?;
|
||||
args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f))
|
||||
}
|
||||
Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f),
|
||||
}
|
||||
self.thir_abstract_const(uv.did)
|
||||
};
|
||||
Ok(ac?.map(|ac| EarlyBinder(ac)))
|
||||
}
|
||||
|
||||
recurse(tcx, ct, &mut f)
|
||||
}
|
||||
pub fn expand_abstract_consts<T: TypeFoldable<'tcx>>(self, ac: T) -> T {
|
||||
struct Expander<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
// We were unable to unify the abstract constant with
|
||||
// a constant found in the caller bounds, there are
|
||||
// now three possible cases here.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum FailureKind {
|
||||
/// The abstract const still references an inference
|
||||
/// variable, in this case we return `TooGeneric`.
|
||||
MentionsInfer,
|
||||
/// The abstract const references a generic parameter,
|
||||
/// this means that we emit an error here.
|
||||
MentionsParam,
|
||||
/// The substs are concrete enough that we can simply
|
||||
/// try and evaluate the given constant.
|
||||
Concrete,
|
||||
impl<'tcx> TypeFolder<'tcx> for Expander<'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) {
|
||||
ty.super_fold_with(self)
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
}
|
||||
fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> {
|
||||
let ct = match c.kind() {
|
||||
ty::ConstKind::Unevaluated(uv) => match self.tcx.bound_abstract_const(uv.def) {
|
||||
Err(e) => self.tcx.const_error_with_guaranteed(c.ty(), e),
|
||||
Ok(Some(bac)) => {
|
||||
let substs = self.tcx.erase_regions(uv.substs);
|
||||
bac.subst(self.tcx, substs)
|
||||
}
|
||||
Ok(None) => c,
|
||||
},
|
||||
_ => c,
|
||||
};
|
||||
ct.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
ac.fold_with(&mut Expander { tcx: self })
|
||||
}
|
||||
}
|
||||
|
@ -345,26 +345,6 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
|
||||
for [ty::abstract_const::Node<'tcx>]
|
||||
{
|
||||
fn decode(decoder: &mut D) -> &'tcx Self {
|
||||
decoder.interner().arena.alloc_from_iter(
|
||||
(0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
|
||||
for [ty::abstract_const::NodeId]
|
||||
{
|
||||
fn decode(decoder: &mut D) -> &'tcx Self {
|
||||
decoder.interner().arena.alloc_from_iter(
|
||||
(0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
|
||||
for ty::List<ty::BoundVariableKind>
|
||||
{
|
||||
@ -376,6 +356,15 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<ty::Const<'tcx>> {
|
||||
fn decode(decoder: &mut D) -> &'tcx Self {
|
||||
let len = decoder.read_usize();
|
||||
decoder
|
||||
.interner()
|
||||
.mk_const_list((0..len).map::<ty::Const<'tcx>, _>(|_| Decodable::decode(decoder)))
|
||||
}
|
||||
}
|
||||
|
||||
impl_decodable_via_ref! {
|
||||
&'tcx ty::TypeckResults<'tcx>,
|
||||
&'tcx ty::List<Ty<'tcx>>,
|
||||
|
@ -1,10 +1,12 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use super::Const;
|
||||
use crate::mir;
|
||||
use crate::mir::interpret::{AllocId, ConstValue, Scalar};
|
||||
use crate::ty::abstract_const::CastKind;
|
||||
use crate::ty::subst::{InternalSubsts, SubstsRef};
|
||||
use crate::ty::ParamEnv;
|
||||
use crate::ty::{self, TyCtxt, TypeVisitable};
|
||||
use crate::ty::{self, List, Ty, TyCtxt, TypeVisitable};
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def_id::DefId;
|
||||
@ -70,8 +72,23 @@ pub enum ConstKind<'tcx> {
|
||||
/// A placeholder for a const which could not be computed; this is
|
||||
/// propagated to avoid useless error messages.
|
||||
Error(ErrorGuaranteed),
|
||||
|
||||
/// Expr which contains an expression which has partially evaluated items.
|
||||
Expr(Expr<'tcx>),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
|
||||
pub enum Expr<'tcx> {
|
||||
Binop(mir::BinOp, Const<'tcx>, Const<'tcx>),
|
||||
UnOp(mir::UnOp, Const<'tcx>),
|
||||
FunctionCall(Const<'tcx>, &'tcx List<Const<'tcx>>),
|
||||
Cast(CastKind, Const<'tcx>, Ty<'tcx>),
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
|
||||
static_assert_size!(Expr<'_>, 24);
|
||||
|
||||
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
|
||||
static_assert_size!(ConstKind<'_>, 32);
|
||||
|
||||
|
@ -137,6 +137,7 @@ pub struct CtxtInterners<'tcx> {
|
||||
// Specifically use a speedy hash algorithm for these hash sets, since
|
||||
// they're accessed quite often.
|
||||
type_: InternedSet<'tcx, WithStableHash<TyS<'tcx>>>,
|
||||
const_lists: InternedSet<'tcx, List<ty::Const<'tcx>>>,
|
||||
substs: InternedSet<'tcx, InternalSubsts<'tcx>>,
|
||||
canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo<'tcx>>>,
|
||||
region: InternedSet<'tcx, RegionKind<'tcx>>,
|
||||
@ -157,6 +158,7 @@ impl<'tcx> CtxtInterners<'tcx> {
|
||||
CtxtInterners {
|
||||
arena,
|
||||
type_: Default::default(),
|
||||
const_lists: Default::default(),
|
||||
substs: Default::default(),
|
||||
region: Default::default(),
|
||||
poly_existential_predicates: Default::default(),
|
||||
@ -2261,6 +2263,7 @@ macro_rules! slice_interners {
|
||||
}
|
||||
|
||||
slice_interners!(
|
||||
const_lists: _intern_const_list(Const<'tcx>),
|
||||
substs: _intern_substs(GenericArg<'tcx>),
|
||||
canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo<'tcx>),
|
||||
poly_existential_predicates:
|
||||
@ -2716,6 +2719,17 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mk_const_list<I: InternAs<ty::Const<'tcx>, &'tcx List<ty::Const<'tcx>>>>(
|
||||
self,
|
||||
iter: I,
|
||||
) -> I::Output {
|
||||
iter.intern_with(|xs| self.intern_const_list(xs))
|
||||
}
|
||||
|
||||
pub fn intern_const_list(self, cs: &[ty::Const<'tcx>]) -> &'tcx List<ty::Const<'tcx>> {
|
||||
if cs.is_empty() { List::empty() } else { self._intern_const_list(cs) }
|
||||
}
|
||||
|
||||
pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx List<Ty<'tcx>> {
|
||||
if ts.is_empty() {
|
||||
List::empty()
|
||||
|
@ -356,7 +356,10 @@ impl DeepRejectCtxt {
|
||||
|
||||
pub fn consts_may_unify(self, obligation_ct: ty::Const<'_>, impl_ct: ty::Const<'_>) -> bool {
|
||||
match impl_ct.kind() {
|
||||
ty::ConstKind::Param(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => {
|
||||
ty::ConstKind::Expr(_)
|
||||
| ty::ConstKind::Param(_)
|
||||
| ty::ConstKind::Unevaluated(_)
|
||||
| ty::ConstKind::Error(_) => {
|
||||
return true;
|
||||
}
|
||||
ty::ConstKind::Value(_) => {}
|
||||
@ -374,7 +377,9 @@ impl DeepRejectCtxt {
|
||||
|
||||
// As we don't necessarily eagerly evaluate constants,
|
||||
// they might unify with any value.
|
||||
ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => true,
|
||||
ty::ConstKind::Expr(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => {
|
||||
true
|
||||
}
|
||||
ty::ConstKind::Value(obl) => match k {
|
||||
ty::ConstKind::Value(imp) => obl == imp,
|
||||
_ => true,
|
||||
|
@ -313,6 +313,26 @@ impl FlagComputation {
|
||||
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
|
||||
}
|
||||
ty::ConstKind::Value(_) => {}
|
||||
ty::ConstKind::Expr(e) => {
|
||||
use ty::Expr;
|
||||
match e {
|
||||
Expr::Binop(_, l, r) => {
|
||||
self.add_const(l);
|
||||
self.add_const(r);
|
||||
}
|
||||
Expr::UnOp(_, v) => self.add_const(v),
|
||||
Expr::FunctionCall(f, args) => {
|
||||
self.add_const(f);
|
||||
for arg in args {
|
||||
self.add_const(arg);
|
||||
}
|
||||
}
|
||||
Expr::Cast(_, c, t) => {
|
||||
self.add_ty(t);
|
||||
self.add_const(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR),
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ pub use self::closure::{
|
||||
CAPTURE_STRUCT_LOCAL,
|
||||
};
|
||||
pub use self::consts::{
|
||||
Const, ConstInt, ConstKind, ConstS, InferConst, ScalarInt, UnevaluatedConst, ValTree,
|
||||
Const, ConstInt, ConstKind, ConstS, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree,
|
||||
};
|
||||
pub use self::context::{
|
||||
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
|
||||
|
@ -4,7 +4,6 @@ use rustc_index::vec::{Idx, IndexVec};
|
||||
|
||||
use crate::middle::exported_symbols::ExportedSymbol;
|
||||
use crate::mir::Body;
|
||||
use crate::ty::abstract_const::Node;
|
||||
use crate::ty::{
|
||||
self, Const, FnSig, GeneratorDiagnosticData, GenericPredicates, Predicate, TraitRef, Ty,
|
||||
};
|
||||
@ -124,6 +123,5 @@ parameterized_over_tcx! {
|
||||
Predicate,
|
||||
GeneratorDiagnosticData,
|
||||
Body,
|
||||
Node,
|
||||
ExportedSymbol,
|
||||
}
|
||||
|
@ -1253,6 +1253,9 @@ pub trait PrettyPrinter<'tcx>:
|
||||
self.pretty_print_bound_var(debruijn, bound_var)?
|
||||
}
|
||||
ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)),
|
||||
// FIXME(generic_const_exprs):
|
||||
// write out some legible representation of an abstract const?
|
||||
ty::ConstKind::Expr(_) => p!("[Const Expr]"),
|
||||
ty::ConstKind::Error(_) => p!("[const error]"),
|
||||
};
|
||||
Ok(self)
|
||||
|
@ -5,7 +5,7 @@
|
||||
//! subtyping, type equality, etc.
|
||||
|
||||
use crate::ty::error::{ExpectedFound, TypeError};
|
||||
use crate::ty::{self, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable};
|
||||
use crate::ty::{self, Expr, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable};
|
||||
use crate::ty::{GenericArg, GenericArgKind, SubstsRef};
|
||||
use rustc_hir as ast;
|
||||
use rustc_hir::def_id::DefId;
|
||||
@ -613,7 +613,10 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
|
||||
if a_ty != b_ty {
|
||||
relation.tcx().sess.delay_span_bug(
|
||||
DUMMY_SP,
|
||||
&format!("cannot relate constants of different types: {} != {}", a_ty, b_ty),
|
||||
&format!(
|
||||
"cannot relate constants ({:?}, {:?}) of different types: {} != {}",
|
||||
a, b, a_ty, b_ty
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -623,11 +626,16 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
|
||||
// an unnormalized (i.e. unevaluated) const in the param-env.
|
||||
// FIXME(generic_const_exprs): Once we always lazily unify unevaluated constants
|
||||
// these `eval` calls can be removed.
|
||||
if !relation.tcx().features().generic_const_exprs {
|
||||
if !tcx.features().generic_const_exprs {
|
||||
a = a.eval(tcx, relation.param_env());
|
||||
b = b.eval(tcx, relation.param_env());
|
||||
}
|
||||
|
||||
if tcx.features().generic_const_exprs {
|
||||
a = tcx.expand_abstract_consts(a);
|
||||
b = tcx.expand_abstract_consts(b);
|
||||
}
|
||||
|
||||
// Currently, the values that can be unified are primitive types,
|
||||
// and those that derive both `PartialEq` and `Eq`, corresponding
|
||||
// to structural-match types.
|
||||
@ -644,16 +652,11 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
|
||||
(ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
|
||||
(ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
|
||||
|
||||
(ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu))
|
||||
if tcx.features().generic_const_exprs =>
|
||||
{
|
||||
tcx.try_unify_abstract_consts(relation.param_env().and((au, bu)))
|
||||
}
|
||||
|
||||
// While this is slightly incorrect, it shouldn't matter for `min_const_generics`
|
||||
// and is the better alternative to waiting until `generic_const_exprs` can
|
||||
// be stabilized.
|
||||
(ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if au.def == bu.def => {
|
||||
assert_eq!(a.ty(), b.ty());
|
||||
let substs = relation.relate_with_variance(
|
||||
ty::Variance::Invariant,
|
||||
ty::VarianceDiagInfo::default(),
|
||||
@ -665,6 +668,50 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
|
||||
a.ty(),
|
||||
));
|
||||
}
|
||||
// Before calling relate on exprs, it is necessary to ensure that the nested consts
|
||||
// have identical types.
|
||||
(ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => {
|
||||
let r = relation;
|
||||
|
||||
// FIXME(generic_const_exprs): is it possible to relate two consts which are not identical
|
||||
// exprs? Should we care about that?
|
||||
let expr = match (ae, be) {
|
||||
(Expr::Binop(a_op, al, ar), Expr::Binop(b_op, bl, br))
|
||||
if a_op == b_op && al.ty() == bl.ty() && ar.ty() == br.ty() =>
|
||||
{
|
||||
Expr::Binop(a_op, r.consts(al, bl)?, r.consts(ar, br)?)
|
||||
}
|
||||
(Expr::UnOp(a_op, av), Expr::UnOp(b_op, bv))
|
||||
if a_op == b_op && av.ty() == bv.ty() =>
|
||||
{
|
||||
Expr::UnOp(a_op, r.consts(av, bv)?)
|
||||
}
|
||||
(Expr::Cast(ak, av, at), Expr::Cast(bk, bv, bt))
|
||||
if ak == bk && av.ty() == bv.ty() =>
|
||||
{
|
||||
Expr::Cast(ak, r.consts(av, bv)?, r.tys(at, bt)?)
|
||||
}
|
||||
(Expr::FunctionCall(af, aa), Expr::FunctionCall(bf, ba))
|
||||
if aa.len() == ba.len()
|
||||
&& af.ty() == bf.ty()
|
||||
&& aa
|
||||
.iter()
|
||||
.zip(ba.iter())
|
||||
.all(|(a_arg, b_arg)| a_arg.ty() == b_arg.ty()) =>
|
||||
{
|
||||
let func = r.consts(af, bf)?;
|
||||
let mut related_args = Vec::with_capacity(aa.len());
|
||||
for (a_arg, b_arg) in aa.iter().zip(ba.iter()) {
|
||||
related_args.push(r.consts(a_arg, b_arg)?);
|
||||
}
|
||||
let related_args = tcx.mk_const_list(related_args.iter());
|
||||
Expr::FunctionCall(func, related_args)
|
||||
}
|
||||
_ => return Err(TypeError::ConstMismatch(expected_found(r, a, b))),
|
||||
};
|
||||
let kind = ty::ConstKind::Expr(expr);
|
||||
return Ok(tcx.mk_const(kind, a.ty()));
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(expected_found(relation, a, b))) }
|
||||
|
@ -601,6 +601,12 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ty::PolyExistentialPredicate<'t
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ty::Const<'tcx>> {
|
||||
fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
ty::util::fold_list(self, folder, |tcx, v| tcx.mk_const_list(v.iter()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ProjectionKind> {
|
||||
fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
ty::util::fold_list(self, folder, |tcx, v| tcx.intern_projs(v))
|
||||
|
@ -214,6 +214,24 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
|
||||
| ty::ConstKind::Value(_)
|
||||
| ty::ConstKind::Error(_) => {}
|
||||
|
||||
ty::ConstKind::Expr(expr) => match expr {
|
||||
ty::Expr::UnOp(_, v) => push_inner(stack, v.into()),
|
||||
ty::Expr::Binop(_, l, r) => {
|
||||
push_inner(stack, r.into());
|
||||
push_inner(stack, l.into())
|
||||
}
|
||||
ty::Expr::FunctionCall(func, args) => {
|
||||
for a in args.iter().rev() {
|
||||
push_inner(stack, a.into());
|
||||
}
|
||||
push_inner(stack, func.into());
|
||||
}
|
||||
ty::Expr::Cast(_, c, t) => {
|
||||
push_inner(stack, t.into());
|
||||
push_inner(stack, c.into());
|
||||
}
|
||||
},
|
||||
|
||||
ty::ConstKind::Unevaluated(ct) => {
|
||||
stack.extend(ct.substs.iter().rev());
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#![feature(control_flow_enum)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(let_chains)]
|
||||
#![recursion_limit = "256"]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
@ -25,7 +26,6 @@ use rustc_middle::bug;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::middle::privacy::{EffectiveVisibilities, Level};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst, Node as ACNode};
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::subst::InternalSubsts;
|
||||
use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind};
|
||||
@ -288,19 +288,8 @@ where
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
self.visit_ty(c.ty())?;
|
||||
let tcx = self.def_id_visitor.tcx();
|
||||
if let Ok(Some(ct)) = AbstractConst::from_const(tcx, c) {
|
||||
walk_abstract_const(tcx, ct, |node| match node.root(tcx) {
|
||||
ACNode::Leaf(leaf) => self.visit_const(leaf),
|
||||
ACNode::Cast(_, _, ty) => self.visit_ty(ty),
|
||||
ACNode::Binop(..) | ACNode::UnaryOp(..) | ACNode::FunctionCall(_, _) => {
|
||||
ControlFlow::CONTINUE
|
||||
}
|
||||
})
|
||||
} else {
|
||||
ControlFlow::CONTINUE
|
||||
}
|
||||
tcx.expand_abstract_consts(c).super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -812,12 +812,6 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [ty::abstract_const::Node<'tcx>] {
|
||||
fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
|
||||
RefDecodable::decode(d)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] {
|
||||
fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
|
||||
RefDecodable::decode(d)
|
||||
|
@ -575,6 +575,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
||||
// a path), even for it we still need to encode a placeholder, as
|
||||
// the path could refer back to e.g. an `impl` using the constant.
|
||||
ty::ConstKind::Unevaluated(_)
|
||||
| ty::ConstKind::Expr(_)
|
||||
| ty::ConstKind::Param(_)
|
||||
| ty::ConstKind::Infer(_)
|
||||
| ty::ConstKind::Bound(..)
|
||||
|
@ -8,152 +8,18 @@
|
||||
//! In this case we try to build an abstract representation of this constant using
|
||||
//! `thir_abstract_const` which can then be checked for structural equality with other
|
||||
//! generic constants mentioned in the `caller_bounds` of the current environment.
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::ty::abstract_const::{
|
||||
walk_abstract_const, AbstractConst, FailureKind, Node, NotConstEvaluatable,
|
||||
};
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
|
||||
use rustc_span::Span;
|
||||
|
||||
use std::iter;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitor};
|
||||
|
||||
use rustc_span::Span;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
pub struct ConstUnifyCtxt<'tcx> {
|
||||
pub tcx: TyCtxt<'tcx>,
|
||||
pub param_env: ty::ParamEnv<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> ConstUnifyCtxt<'tcx> {
|
||||
// Substitutes generics repeatedly to allow AbstractConsts to unify where a
|
||||
// ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g.
|
||||
// Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])]
|
||||
#[inline]
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn try_replace_substs_in_root(
|
||||
&self,
|
||||
mut abstr_const: AbstractConst<'tcx>,
|
||||
) -> Option<AbstractConst<'tcx>> {
|
||||
while let Node::Leaf(ct) = abstr_const.root(self.tcx) {
|
||||
match AbstractConst::from_const(self.tcx, ct) {
|
||||
Ok(Some(act)) => abstr_const = act,
|
||||
Ok(None) => break,
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
|
||||
Some(abstr_const)
|
||||
}
|
||||
|
||||
/// Tries to unify two abstract constants using structural equality.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool {
|
||||
let a = if let Some(a) = self.try_replace_substs_in_root(a) {
|
||||
a
|
||||
} else {
|
||||
return true;
|
||||
};
|
||||
|
||||
let b = if let Some(b) = self.try_replace_substs_in_root(b) {
|
||||
b
|
||||
} else {
|
||||
return true;
|
||||
};
|
||||
|
||||
let a_root = a.root(self.tcx);
|
||||
let b_root = b.root(self.tcx);
|
||||
debug!(?a_root, ?b_root);
|
||||
|
||||
match (a_root, b_root) {
|
||||
(Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
|
||||
let a_ct = a_ct.eval(self.tcx, self.param_env);
|
||||
debug!("a_ct evaluated: {:?}", a_ct);
|
||||
let b_ct = b_ct.eval(self.tcx, self.param_env);
|
||||
debug!("b_ct evaluated: {:?}", b_ct);
|
||||
|
||||
if a_ct.ty() != b_ct.ty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
match (a_ct.kind(), b_ct.kind()) {
|
||||
// We can just unify errors with everything to reduce the amount of
|
||||
// emitted errors here.
|
||||
(ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true,
|
||||
(ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => {
|
||||
a_param == b_param
|
||||
}
|
||||
(ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
|
||||
// If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]`
|
||||
// we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This
|
||||
// means that we only allow inference variables if they are equal.
|
||||
(ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val,
|
||||
// We expand generic anonymous constants at the start of this function, so this
|
||||
// branch should only be taking when dealing with associated constants, at
|
||||
// which point directly comparing them seems like the desired behavior.
|
||||
//
|
||||
// FIXME(generic_const_exprs): This isn't actually the case.
|
||||
// We also take this branch for concrete anonymous constants and
|
||||
// expand generic anonymous constants with concrete substs.
|
||||
(ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => {
|
||||
a_uv == b_uv
|
||||
}
|
||||
// FIXME(generic_const_exprs): We may want to either actually try
|
||||
// to evaluate `a_ct` and `b_ct` if they are fully concrete or something like
|
||||
// this, for now we just return false here.
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
(Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => {
|
||||
self.try_unify(a.subtree(al), b.subtree(bl))
|
||||
&& self.try_unify(a.subtree(ar), b.subtree(br))
|
||||
}
|
||||
(Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => {
|
||||
self.try_unify(a.subtree(av), b.subtree(bv))
|
||||
}
|
||||
(Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args))
|
||||
if a_args.len() == b_args.len() =>
|
||||
{
|
||||
self.try_unify(a.subtree(a_f), b.subtree(b_f))
|
||||
&& iter::zip(a_args, b_args)
|
||||
.all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn)))
|
||||
}
|
||||
(Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty))
|
||||
if (a_ty == b_ty) && (a_kind == b_kind) =>
|
||||
{
|
||||
self.try_unify(a.subtree(a_operand), b.subtree(b_operand))
|
||||
}
|
||||
// use this over `_ => false` to make adding variants to `Node` less error prone
|
||||
(Node::Cast(..), _)
|
||||
| (Node::FunctionCall(..), _)
|
||||
| (Node::UnaryOp(..), _)
|
||||
| (Node::Binop(..), _)
|
||||
| (Node::Leaf(..), _) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
pub fn try_unify_abstract_consts<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
(a, b): (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>),
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> bool {
|
||||
(|| {
|
||||
if let Some(a) = AbstractConst::new(tcx, a)? {
|
||||
if let Some(b) = AbstractConst::new(tcx, b)? {
|
||||
let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
|
||||
return Ok(const_unify_ctxt.try_unify(a, b));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
})()
|
||||
.unwrap_or_else(|_: ErrorGuaranteed| true)
|
||||
// FIXME(generic_const_exprs): We should instead have this
|
||||
// method return the resulting `ty::Const` and return `ConstKind::Error`
|
||||
// on `ErrorGuaranteed`.
|
||||
}
|
||||
use crate::traits::ObligationCtxt;
|
||||
|
||||
/// Check if a given constant can be evaluated.
|
||||
#[instrument(skip(infcx), level = "debug")]
|
||||
@ -166,6 +32,8 @@ pub fn is_const_evaluatable<'tcx>(
|
||||
let tcx = infcx.tcx;
|
||||
let uv = match ct.kind() {
|
||||
ty::ConstKind::Unevaluated(uv) => uv,
|
||||
// FIXME(generic_const_exprs): this seems wrong but I couldn't find a way to get this to trigger
|
||||
ty::ConstKind::Expr(_) => bug!("unexpected expr in `is_const_evaluatable: {ct:?}"),
|
||||
ty::ConstKind::Param(_)
|
||||
| ty::ConstKind::Bound(_, _)
|
||||
| ty::ConstKind::Placeholder(_)
|
||||
@ -175,21 +43,25 @@ pub fn is_const_evaluatable<'tcx>(
|
||||
};
|
||||
|
||||
if tcx.features().generic_const_exprs {
|
||||
if let Some(ct) = AbstractConst::new(tcx, uv)? {
|
||||
if satisfied_from_param_env(tcx, ct, param_env)? {
|
||||
let ct = tcx.expand_abstract_consts(ct);
|
||||
|
||||
let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() {
|
||||
tcx.def_kind(uv.def.did) == DefKind::AnonConst
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !is_anon_ct {
|
||||
if satisfied_from_param_env(tcx, infcx, ct, param_env) {
|
||||
return Ok(());
|
||||
}
|
||||
match ct.unify_failure_kind(tcx) {
|
||||
FailureKind::MentionsInfer => {
|
||||
return Err(NotConstEvaluatable::MentionsInfer);
|
||||
}
|
||||
FailureKind::MentionsParam => {
|
||||
return Err(NotConstEvaluatable::MentionsParam);
|
||||
}
|
||||
// returned below
|
||||
FailureKind::Concrete => {}
|
||||
if ct.has_non_region_infer() {
|
||||
return Err(NotConstEvaluatable::MentionsInfer);
|
||||
} else if ct.has_non_region_param() {
|
||||
return Err(NotConstEvaluatable::MentionsParam);
|
||||
}
|
||||
}
|
||||
|
||||
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
|
||||
match concrete {
|
||||
Err(ErrorHandled::TooGeneric) => Err(NotConstEvaluatable::Error(
|
||||
@ -211,28 +83,33 @@ pub fn is_const_evaluatable<'tcx>(
|
||||
//
|
||||
// See #74595 for more details about this.
|
||||
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
|
||||
|
||||
match concrete {
|
||||
// If we're evaluating a foreign constant, under a nightly compiler without generic
|
||||
// const exprs, AND it would've passed if that expression had been evaluated with
|
||||
// generic const exprs, then suggest using generic const exprs.
|
||||
Err(_) if tcx.sess.is_nightly_build()
|
||||
&& let Ok(Some(ct)) = AbstractConst::new(tcx, uv)
|
||||
&& satisfied_from_param_env(tcx, ct, param_env) == Ok(true) => {
|
||||
tcx.sess
|
||||
.struct_span_fatal(
|
||||
// Slightly better span than just using `span` alone
|
||||
if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
|
||||
"failed to evaluate generic const expression",
|
||||
)
|
||||
.note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
|
||||
.span_suggestion_verbose(
|
||||
rustc_span::DUMMY_SP,
|
||||
"consider enabling this feature",
|
||||
"#![feature(generic_const_exprs)]\n",
|
||||
rustc_errors::Applicability::MaybeIncorrect,
|
||||
)
|
||||
.emit()
|
||||
// If we're evaluating a generic foreign constant, under a nightly compiler while
|
||||
// the current crate does not enable `feature(generic_const_exprs)`, abort
|
||||
// compilation with a useful error.
|
||||
Err(_)
|
||||
if tcx.sess.is_nightly_build()
|
||||
&& satisfied_from_param_env(
|
||||
tcx,
|
||||
infcx,
|
||||
tcx.expand_abstract_consts(ct),
|
||||
param_env,
|
||||
) =>
|
||||
{
|
||||
tcx.sess
|
||||
.struct_span_fatal(
|
||||
// Slightly better span than just using `span` alone
|
||||
if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
|
||||
"failed to evaluate generic const expression",
|
||||
)
|
||||
.note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
|
||||
.span_suggestion_verbose(
|
||||
rustc_span::DUMMY_SP,
|
||||
"consider enabling this feature",
|
||||
"#![feature(generic_const_exprs)]\n",
|
||||
rustc_errors::Applicability::MaybeIncorrect,
|
||||
)
|
||||
.emit()
|
||||
}
|
||||
|
||||
Err(ErrorHandled::TooGeneric) => {
|
||||
@ -241,49 +118,82 @@ pub fn is_const_evaluatable<'tcx>(
|
||||
} else if uv.has_non_region_param() {
|
||||
NotConstEvaluatable::MentionsParam
|
||||
} else {
|
||||
let guar = infcx.tcx.sess.delay_span_bug(span, format!("Missing value for constant, but no error reported?"));
|
||||
let guar = infcx.tcx.sess.delay_span_bug(
|
||||
span,
|
||||
format!("Missing value for constant, but no error reported?"),
|
||||
);
|
||||
NotConstEvaluatable::Error(guar)
|
||||
};
|
||||
|
||||
Err(err)
|
||||
},
|
||||
}
|
||||
Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
|
||||
Ok(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
#[instrument(skip(infcx, tcx), level = "debug")]
|
||||
fn satisfied_from_param_env<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ct: AbstractConst<'tcx>,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
ct: ty::Const<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Result<bool, NotConstEvaluatable> {
|
||||
) -> bool {
|
||||
// Try to unify with each subtree in the AbstractConst to allow for
|
||||
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
|
||||
// predicate for `(N + 1) * 2`
|
||||
struct Visitor<'a, 'tcx> {
|
||||
ct: ty::Const<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
}
|
||||
impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> {
|
||||
type BreakTy = ();
|
||||
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
if let Ok(()) = self.infcx.commit_if_ok(|_| {
|
||||
let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
|
||||
if let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty())
|
||||
&& let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct)
|
||||
&& ocx.select_all_or_error().is_empty()
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}) {
|
||||
ControlFlow::BREAK
|
||||
} else if let ty::ConstKind::Expr(e) = c.kind() {
|
||||
e.visit_with(self)
|
||||
} else {
|
||||
// FIXME(generic_const_exprs): This doesn't recurse into `<T as Trait<U>>::ASSOC`'s substs.
|
||||
// This is currently unobservable as `<T as Trait<{ U + 1 }>>::ASSOC` creates an anon const
|
||||
// with its own `ConstEvaluatable` bound in the param env which we will visit separately.
|
||||
//
|
||||
// If we start allowing directly writing `ConstKind::Expr` without an intermediate anon const
|
||||
// this will be incorrect. It might be worth investigating making `predicates_of` elaborate
|
||||
// all of the `ConstEvaluatable` bounds rather than having a visitor here.
|
||||
ControlFlow::CONTINUE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for pred in param_env.caller_bounds() {
|
||||
match pred.kind().skip_binder() {
|
||||
ty::PredicateKind::ConstEvaluatable(uv) => {
|
||||
if let Some(b_ct) = AbstractConst::from_const(tcx, uv)? {
|
||||
let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
|
||||
ty::PredicateKind::ConstEvaluatable(ce) => {
|
||||
let b_ct = tcx.expand_abstract_consts(ce);
|
||||
let mut v = Visitor { ct, infcx, param_env };
|
||||
let result = b_ct.visit_with(&mut v);
|
||||
|
||||
// Try to unify with each subtree in the AbstractConst to allow for
|
||||
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
|
||||
// predicate for `(N + 1) * 2`
|
||||
let result = walk_abstract_const(tcx, b_ct, |b_ct| {
|
||||
match const_unify_ctxt.try_unify(ct, b_ct) {
|
||||
true => ControlFlow::BREAK,
|
||||
false => ControlFlow::CONTINUE,
|
||||
}
|
||||
});
|
||||
|
||||
if let ControlFlow::Break(()) = result {
|
||||
debug!("is_const_evaluatable: abstract_const ~~> ok");
|
||||
return Ok(true);
|
||||
}
|
||||
if let ControlFlow::Break(()) = result {
|
||||
debug!("is_const_evaluatable: abstract_const ~~> ok");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {} // don't care
|
||||
}
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
false
|
||||
}
|
||||
|
@ -455,20 +455,47 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
|
||||
}
|
||||
|
||||
ty::PredicateKind::ConstEquate(c1, c2) => {
|
||||
let tcx = self.selcx.tcx();
|
||||
assert!(
|
||||
self.selcx.tcx().features().generic_const_exprs,
|
||||
tcx.features().generic_const_exprs,
|
||||
"`ConstEquate` without a feature gate: {c1:?} {c2:?}",
|
||||
);
|
||||
debug!(?c1, ?c2, "equating consts");
|
||||
// FIXME: we probably should only try to unify abstract constants
|
||||
// if the constants depend on generic parameters.
|
||||
//
|
||||
// Let's just see where this breaks :shrug:
|
||||
if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
|
||||
(c1.kind(), c2.kind())
|
||||
{
|
||||
if infcx.try_unify_abstract_consts(a, b, obligation.param_env) {
|
||||
return ProcessResult::Changed(vec![]);
|
||||
let c1 = tcx.expand_abstract_consts(c1);
|
||||
let c2 = tcx.expand_abstract_consts(c2);
|
||||
debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2);
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
use ty::ConstKind::Unevaluated;
|
||||
match (c1.kind(), c2.kind()) {
|
||||
(Unevaluated(a), Unevaluated(b))
|
||||
if a.def.did == b.def.did
|
||||
&& tcx.def_kind(a.def.did) == DefKind::AssocConst =>
|
||||
{
|
||||
if let Ok(new_obligations) = infcx
|
||||
.at(&obligation.cause, obligation.param_env)
|
||||
.trace(c1, c2)
|
||||
.eq(a.substs, b.substs)
|
||||
{
|
||||
return ProcessResult::Changed(mk_pending(
|
||||
new_obligations.into_obligations(),
|
||||
));
|
||||
}
|
||||
}
|
||||
(_, Unevaluated(_)) | (Unevaluated(_), _) => (),
|
||||
(_, _) => {
|
||||
if let Ok(new_obligations) =
|
||||
infcx.at(&obligation.cause, obligation.param_env).eq(c1, c2)
|
||||
{
|
||||
return ProcessResult::Changed(mk_pending(
|
||||
new_obligations.into_obligations(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -508,7 +535,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
|
||||
.at(&obligation.cause, obligation.param_env)
|
||||
.eq(c1, c2)
|
||||
{
|
||||
Ok(_) => ProcessResult::Changed(vec![]),
|
||||
Ok(inf_ok) => {
|
||||
ProcessResult::Changed(mk_pending(inf_ok.into_obligations()))
|
||||
}
|
||||
Err(err) => ProcessResult::Error(
|
||||
FulfillmentErrorCode::CodeConstEquateError(
|
||||
ExpectedFound::new(true, c1, c2),
|
||||
|
@ -932,10 +932,6 @@ pub fn provide(providers: &mut ty::query::Providers) {
|
||||
vtable_trait_upcasting_coercion_new_vptr_slot,
|
||||
subst_and_check_impossible_predicates,
|
||||
is_impossible_method,
|
||||
try_unify_abstract_consts: |tcx, param_env_and| {
|
||||
let (param_env, (a, b)) = param_env_and.into_parts();
|
||||
const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env)
|
||||
},
|
||||
..*providers
|
||||
};
|
||||
}
|
||||
|
@ -17,11 +17,10 @@ use hir::def::DefKind;
|
||||
use rustc_errors::{DelayDm, FatalError, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst};
|
||||
use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
|
||||
use rustc_middle::ty::{
|
||||
self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
|
||||
};
|
||||
use rustc_middle::ty::{GenericArg, InternalSubsts};
|
||||
use rustc_middle::ty::{Predicate, ToPredicate};
|
||||
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
|
||||
use rustc_span::symbol::Symbol;
|
||||
@ -837,23 +836,9 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>(
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// Constants can only influence object safety if they reference `Self`.
|
||||
// Constants can only influence object safety if they are generic and reference `Self`.
|
||||
// This is only possible for unevaluated constants, so we walk these here.
|
||||
//
|
||||
// If `AbstractConst::from_const` returned an error we already failed compilation
|
||||
// so we don't have to emit an additional error here.
|
||||
use rustc_middle::ty::abstract_const::Node;
|
||||
if let Ok(Some(ct)) = AbstractConst::from_const(self.tcx, ct) {
|
||||
walk_abstract_const(self.tcx, ct, |node| match node.root(self.tcx) {
|
||||
Node::Leaf(leaf) => self.visit_const(leaf),
|
||||
Node::Cast(_, _, ty) => self.visit_ty(ty),
|
||||
Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => {
|
||||
ControlFlow::CONTINUE
|
||||
}
|
||||
})
|
||||
} else {
|
||||
ct.super_visit_with(self)
|
||||
}
|
||||
self.tcx.expand_abstract_consts(ct).super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -657,21 +657,62 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
}
|
||||
|
||||
ty::PredicateKind::ConstEquate(c1, c2) => {
|
||||
let tcx = self.tcx();
|
||||
assert!(
|
||||
self.tcx().features().generic_const_exprs,
|
||||
tcx.features().generic_const_exprs,
|
||||
"`ConstEquate` without a feature gate: {c1:?} {c2:?}",
|
||||
);
|
||||
debug!(?c1, ?c2, "evaluate_predicate_recursively: equating consts");
|
||||
|
||||
// FIXME: we probably should only try to unify abstract constants
|
||||
// if the constants depend on generic parameters.
|
||||
//
|
||||
// Let's just see where this breaks :shrug:
|
||||
if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
|
||||
(c1.kind(), c2.kind())
|
||||
{
|
||||
if self.infcx.try_unify_abstract_consts(a, b, obligation.param_env) {
|
||||
return Ok(EvaluatedToOk);
|
||||
let c1 = tcx.expand_abstract_consts(c1);
|
||||
let c2 = tcx.expand_abstract_consts(c2);
|
||||
debug!(
|
||||
"evalaute_predicate_recursively: equating consts:\nc1= {:?}\nc2= {:?}",
|
||||
c1, c2
|
||||
);
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
use ty::ConstKind::Unevaluated;
|
||||
match (c1.kind(), c2.kind()) {
|
||||
(Unevaluated(a), Unevaluated(b))
|
||||
if a.def.did == b.def.did
|
||||
&& tcx.def_kind(a.def.did) == DefKind::AssocConst =>
|
||||
{
|
||||
if let Ok(new_obligations) = self
|
||||
.infcx
|
||||
.at(&obligation.cause, obligation.param_env)
|
||||
.trace(c1, c2)
|
||||
.eq(a.substs, b.substs)
|
||||
{
|
||||
let mut obligations = new_obligations.obligations;
|
||||
self.add_depth(
|
||||
obligations.iter_mut(),
|
||||
obligation.recursion_depth,
|
||||
);
|
||||
return self.evaluate_predicates_recursively(
|
||||
previous_stack,
|
||||
obligations.into_iter(),
|
||||
);
|
||||
}
|
||||
}
|
||||
(_, Unevaluated(_)) | (Unevaluated(_), _) => (),
|
||||
(_, _) => {
|
||||
if let Ok(new_obligations) = self
|
||||
.infcx
|
||||
.at(&obligation.cause, obligation.param_env)
|
||||
.eq(c1, c2)
|
||||
{
|
||||
let mut obligations = new_obligations.obligations;
|
||||
self.add_depth(
|
||||
obligations.iter_mut(),
|
||||
obligation.recursion_depth,
|
||||
);
|
||||
return self.evaluate_predicates_recursively(
|
||||
previous_stack,
|
||||
obligations.into_iter(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -698,7 +739,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
.at(&obligation.cause, obligation.param_env)
|
||||
.eq(c1, c2)
|
||||
{
|
||||
Ok(_) => Ok(EvaluatedToOk),
|
||||
Ok(inf_ok) => self.evaluate_predicates_recursively(
|
||||
previous_stack,
|
||||
inf_ok.into_obligations(),
|
||||
),
|
||||
Err(_) => Ok(EvaluatedToErr),
|
||||
}
|
||||
}
|
||||
|
@ -476,6 +476,11 @@ impl<'tcx> WfPredicates<'tcx> {
|
||||
ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())),
|
||||
));
|
||||
}
|
||||
// FIXME(generic_const_exprs): This seems wrong but I could not find a way to get this to trigger
|
||||
ty::ConstKind::Expr(_) => {
|
||||
bug!("checking wfness of `ConstKind::Expr` is unsupported")
|
||||
}
|
||||
|
||||
ty::ConstKind::Error(_)
|
||||
| ty::ConstKind::Param(_)
|
||||
| ty::ConstKind::Bound(..)
|
||||
|
@ -1,10 +1,11 @@
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
|
||||
use rustc_middle::ty::abstract_const::{CastKind, Node, NodeId};
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
|
||||
use rustc_middle::thir::visit;
|
||||
use rustc_middle::thir::visit::Visitor;
|
||||
use rustc_middle::ty::abstract_const::CastKind;
|
||||
use rustc_middle::ty::{self, ConstKind, Expr, TyCtxt, TypeVisitable};
|
||||
use rustc_middle::{mir, thir};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::VariantIdx;
|
||||
@ -76,334 +77,286 @@ pub(crate) fn destructure_const<'tcx>(
|
||||
ty::DestructuredConst { variant, fields }
|
||||
}
|
||||
|
||||
pub struct AbstractConstBuilder<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body_id: thir::ExprId,
|
||||
body: &'a thir::Thir<'tcx>,
|
||||
/// The current WIP node tree.
|
||||
nodes: IndexVec<NodeId, Node<'tcx>>,
|
||||
/// We do not allow all binary operations in abstract consts, so filter disallowed ones.
|
||||
fn check_binop(op: mir::BinOp) -> bool {
|
||||
use mir::BinOp::*;
|
||||
match op {
|
||||
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le | Ne
|
||||
| Ge | Gt => true,
|
||||
Offset => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
||||
fn root_span(&self) -> Span {
|
||||
self.body.exprs[self.body_id].span
|
||||
/// While we currently allow all unary operations, we still want to explicitly guard against
|
||||
/// future changes here.
|
||||
fn check_unop(op: mir::UnOp) -> bool {
|
||||
use mir::UnOp::*;
|
||||
match op {
|
||||
Not | Neg => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&mut self, sub: GenericConstantTooComplexSub) -> Result<!, ErrorGuaranteed> {
|
||||
let reported = self.tcx.sess.emit_err(GenericConstantTooComplex {
|
||||
span: self.root_span(),
|
||||
maybe_supported: None,
|
||||
sub,
|
||||
});
|
||||
fn recurse_build<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &thir::Thir<'tcx>,
|
||||
node: thir::ExprId,
|
||||
root_span: Span,
|
||||
) -> Result<ty::Const<'tcx>, ErrorGuaranteed> {
|
||||
use thir::ExprKind;
|
||||
let node = &body.exprs[node];
|
||||
|
||||
Err(reported)
|
||||
}
|
||||
let maybe_supported_error = |a| maybe_supported_error(tcx, a, root_span);
|
||||
let error = |a| error(tcx, a, root_span);
|
||||
|
||||
fn maybe_supported_error(
|
||||
&mut self,
|
||||
sub: GenericConstantTooComplexSub,
|
||||
) -> Result<!, ErrorGuaranteed> {
|
||||
let reported = self.tcx.sess.emit_err(GenericConstantTooComplex {
|
||||
span: self.root_span(),
|
||||
maybe_supported: Some(()),
|
||||
sub,
|
||||
});
|
||||
|
||||
Err(reported)
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx, body, body_id), level = "debug")]
|
||||
pub fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
(body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId),
|
||||
) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> {
|
||||
let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() };
|
||||
|
||||
struct IsThirPolymorphic<'a, 'tcx> {
|
||||
is_poly: bool,
|
||||
thir: &'a thir::Thir<'tcx>,
|
||||
Ok(match &node.kind {
|
||||
// I dont know if handling of these 3 is correct
|
||||
&ExprKind::Scope { value, .. } => recurse_build(tcx, body, value, root_span)?,
|
||||
&ExprKind::PlaceTypeAscription { source, .. }
|
||||
| &ExprKind::ValueTypeAscription { source, .. } => {
|
||||
recurse_build(tcx, body, source, root_span)?
|
||||
}
|
||||
|
||||
use crate::rustc_middle::thir::visit::Visitor;
|
||||
use thir::visit;
|
||||
|
||||
impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
|
||||
fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool {
|
||||
if expr.ty.has_non_region_param() {
|
||||
return true;
|
||||
&ExprKind::Literal { lit, neg } => {
|
||||
let sp = node.span;
|
||||
match tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) {
|
||||
Ok(c) => c,
|
||||
Err(LitToConstError::Reported(guar)) => {
|
||||
tcx.const_error_with_guaranteed(node.ty, guar)
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
thir::ExprKind::NamedConst { substs, .. } => substs.has_non_region_param(),
|
||||
thir::ExprKind::ConstParam { .. } => true,
|
||||
thir::ExprKind::Repeat { value, count } => {
|
||||
self.visit_expr(&self.thir()[value]);
|
||||
count.has_non_region_param()
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool {
|
||||
if pat.ty.has_non_region_param() {
|
||||
return true;
|
||||
}
|
||||
|
||||
match pat.kind {
|
||||
thir::PatKind::Constant { value } => value.has_non_region_param(),
|
||||
thir::PatKind::Range(box thir::PatRange { lo, hi, .. }) => {
|
||||
lo.has_non_region_param() || hi.has_non_region_param()
|
||||
}
|
||||
_ => false,
|
||||
Err(LitToConstError::TypeError) => {
|
||||
bug!("encountered type error in lit_to_const")
|
||||
}
|
||||
}
|
||||
}
|
||||
&ExprKind::NonHirLiteral { lit, user_ty: _ } => {
|
||||
let val = ty::ValTree::from_scalar_int(lit);
|
||||
ty::Const::from_value(tcx, val, node.ty)
|
||||
}
|
||||
&ExprKind::ZstLiteral { user_ty: _ } => {
|
||||
let val = ty::ValTree::zst();
|
||||
ty::Const::from_value(tcx, val, node.ty)
|
||||
}
|
||||
&ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
|
||||
let uneval = ty::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs);
|
||||
tcx.mk_const(ty::ConstKind::Unevaluated(uneval), node.ty)
|
||||
}
|
||||
ExprKind::ConstParam { param, .. } => tcx.mk_const(ty::ConstKind::Param(*param), node.ty),
|
||||
|
||||
impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
|
||||
fn thir(&self) -> &'a thir::Thir<'tcx> {
|
||||
&self.thir
|
||||
ExprKind::Call { fun, args, .. } => {
|
||||
let fun = recurse_build(tcx, body, *fun, root_span)?;
|
||||
|
||||
let mut new_args = Vec::<ty::Const<'tcx>>::with_capacity(args.len());
|
||||
for &id in args.iter() {
|
||||
new_args.push(recurse_build(tcx, body, id, root_span)?);
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) {
|
||||
self.is_poly |= self.expr_is_poly(expr);
|
||||
if !self.is_poly {
|
||||
visit::walk_expr(self, expr)
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) {
|
||||
self.is_poly |= self.pat_is_poly(pat);
|
||||
if !self.is_poly {
|
||||
visit::walk_pat(self, pat);
|
||||
}
|
||||
let new_args = tcx.mk_const_list(new_args.iter());
|
||||
tcx.mk_const(ConstKind::Expr(Expr::FunctionCall(fun, new_args)), node.ty)
|
||||
}
|
||||
&ExprKind::Binary { op, lhs, rhs } if check_binop(op) => {
|
||||
let lhs = recurse_build(tcx, body, lhs, root_span)?;
|
||||
let rhs = recurse_build(tcx, body, rhs, root_span)?;
|
||||
tcx.mk_const(ConstKind::Expr(Expr::Binop(op, lhs, rhs)), node.ty)
|
||||
}
|
||||
&ExprKind::Unary { op, arg } if check_unop(op) => {
|
||||
let arg = recurse_build(tcx, body, arg, root_span)?;
|
||||
tcx.mk_const(ConstKind::Expr(Expr::UnOp(op, arg)), node.ty)
|
||||
}
|
||||
// This is necessary so that the following compiles:
|
||||
//
|
||||
// ```
|
||||
// fn foo<const N: usize>(a: [(); N + 1]) {
|
||||
// bar::<{ N + 1 }>();
|
||||
// }
|
||||
// ```
|
||||
ExprKind::Block { block } => {
|
||||
if let thir::Block { stmts: box [], expr: Some(e), .. } = &body.blocks[*block] {
|
||||
recurse_build(tcx, body, *e, root_span)?
|
||||
} else {
|
||||
maybe_supported_error(GenericConstantTooComplexSub::BlockNotSupported(node.span))?
|
||||
}
|
||||
}
|
||||
// `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a
|
||||
// "coercion cast" i.e. using a coercion or is a no-op.
|
||||
// This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
|
||||
&ExprKind::Use { source } => {
|
||||
let arg = recurse_build(tcx, body, source, root_span)?;
|
||||
tcx.mk_const(ConstKind::Expr(Expr::Cast(CastKind::Use, arg, node.ty)), node.ty)
|
||||
}
|
||||
&ExprKind::Cast { source } => {
|
||||
let arg = recurse_build(tcx, body, source, root_span)?;
|
||||
tcx.mk_const(ConstKind::Expr(Expr::Cast(CastKind::As, arg, node.ty)), node.ty)
|
||||
}
|
||||
ExprKind::Borrow { arg, .. } => {
|
||||
let arg_node = &body.exprs[*arg];
|
||||
|
||||
let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body };
|
||||
visit::walk_expr(&mut is_poly_vis, &body[body_id]);
|
||||
debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly);
|
||||
if !is_poly_vis.is_poly {
|
||||
return Ok(None);
|
||||
// Skip reborrows for now until we allow Deref/Borrow/AddressOf
|
||||
// expressions.
|
||||
// FIXME(generic_const_exprs): Verify/explain why this is sound
|
||||
if let ExprKind::Deref { arg } = arg_node.kind {
|
||||
recurse_build(tcx, body, arg, root_span)?
|
||||
} else {
|
||||
maybe_supported_error(GenericConstantTooComplexSub::BorrowNotSupported(node.span))?
|
||||
}
|
||||
}
|
||||
// FIXME(generic_const_exprs): We may want to support these.
|
||||
ExprKind::AddressOf { .. } | ExprKind::Deref { .. } => maybe_supported_error(
|
||||
GenericConstantTooComplexSub::AddressAndDerefNotSupported(node.span),
|
||||
)?,
|
||||
ExprKind::Repeat { .. } | ExprKind::Array { .. } => {
|
||||
maybe_supported_error(GenericConstantTooComplexSub::ArrayNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::NeverToAny { .. } => {
|
||||
maybe_supported_error(GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Tuple { .. } => {
|
||||
maybe_supported_error(GenericConstantTooComplexSub::TupleNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Index { .. } => {
|
||||
maybe_supported_error(GenericConstantTooComplexSub::IndexNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Field { .. } => {
|
||||
maybe_supported_error(GenericConstantTooComplexSub::FieldNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::ConstBlock { .. } => {
|
||||
maybe_supported_error(GenericConstantTooComplexSub::ConstBlockNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Adt(_) => {
|
||||
maybe_supported_error(GenericConstantTooComplexSub::AdtNotSupported(node.span))?
|
||||
}
|
||||
// dont know if this is correct
|
||||
ExprKind::Pointer { .. } => {
|
||||
error(GenericConstantTooComplexSub::PointerNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Yield { .. } => {
|
||||
error(GenericConstantTooComplexSub::YieldNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => {
|
||||
error(GenericConstantTooComplexSub::LoopNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Box { .. } => error(GenericConstantTooComplexSub::BoxNotSupported(node.span))?,
|
||||
|
||||
ExprKind::Unary { .. } => unreachable!(),
|
||||
// we handle valid unary/binary ops above
|
||||
ExprKind::Binary { .. } => {
|
||||
error(GenericConstantTooComplexSub::BinaryNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::LogicalOp { .. } => {
|
||||
error(GenericConstantTooComplexSub::LogicalOpNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
|
||||
error(GenericConstantTooComplexSub::AssignNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Closure { .. } | ExprKind::Return { .. } => {
|
||||
error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))?
|
||||
}
|
||||
// let expressions imply control flow
|
||||
ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => {
|
||||
error(GenericConstantTooComplexSub::ControlFlowNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::InlineAsm { .. } => {
|
||||
error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))?
|
||||
}
|
||||
|
||||
Ok(Some(builder))
|
||||
// we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
|
||||
ExprKind::VarRef { .. }
|
||||
| ExprKind::UpvarRef { .. }
|
||||
| ExprKind::StaticRef { .. }
|
||||
| ExprKind::ThreadLocalRef(_) => {
|
||||
error(GenericConstantTooComplexSub::OperationNotSupported(node.span))?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
struct IsThirPolymorphic<'a, 'tcx> {
|
||||
is_poly: bool,
|
||||
thir: &'a thir::Thir<'tcx>,
|
||||
}
|
||||
|
||||
fn error<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
sub: GenericConstantTooComplexSub,
|
||||
root_span: Span,
|
||||
) -> Result<!, ErrorGuaranteed> {
|
||||
let reported = tcx.sess.emit_err(GenericConstantTooComplex {
|
||||
span: root_span,
|
||||
maybe_supported: None,
|
||||
sub,
|
||||
});
|
||||
|
||||
Err(reported)
|
||||
}
|
||||
|
||||
fn maybe_supported_error<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
sub: GenericConstantTooComplexSub,
|
||||
root_span: Span,
|
||||
) -> Result<!, ErrorGuaranteed> {
|
||||
let reported = tcx.sess.emit_err(GenericConstantTooComplex {
|
||||
span: root_span,
|
||||
maybe_supported: Some(()),
|
||||
sub,
|
||||
});
|
||||
|
||||
Err(reported)
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
|
||||
fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool {
|
||||
if expr.ty.has_non_region_param() {
|
||||
return true;
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
thir::ExprKind::NamedConst { substs, .. } => substs.has_non_region_param(),
|
||||
thir::ExprKind::ConstParam { .. } => true,
|
||||
thir::ExprKind::Repeat { value, count } => {
|
||||
self.visit_expr(&self.thir()[value]);
|
||||
count.has_non_region_param()
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool {
|
||||
if pat.ty.has_non_region_param() {
|
||||
return true;
|
||||
}
|
||||
|
||||
match pat.kind {
|
||||
thir::PatKind::Constant { value } => value.has_non_region_param(),
|
||||
thir::PatKind::Range(box thir::PatRange { lo, hi, .. }) => {
|
||||
lo.has_non_region_param() || hi.has_non_region_param()
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
|
||||
fn thir(&self) -> &'a thir::Thir<'tcx> {
|
||||
&self.thir
|
||||
}
|
||||
|
||||
/// We do not allow all binary operations in abstract consts, so filter disallowed ones.
|
||||
fn check_binop(op: mir::BinOp) -> bool {
|
||||
use mir::BinOp::*;
|
||||
match op {
|
||||
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le
|
||||
| Ne | Ge | Gt => true,
|
||||
Offset => false,
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) {
|
||||
self.is_poly |= self.expr_is_poly(expr);
|
||||
if !self.is_poly {
|
||||
visit::walk_expr(self, expr)
|
||||
}
|
||||
}
|
||||
|
||||
/// While we currently allow all unary operations, we still want to explicitly guard against
|
||||
/// future changes here.
|
||||
fn check_unop(op: mir::UnOp) -> bool {
|
||||
use mir::UnOp::*;
|
||||
match op {
|
||||
Not | Neg => true,
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) {
|
||||
self.is_poly |= self.pat_is_poly(pat);
|
||||
if !self.is_poly {
|
||||
visit::walk_pat(self, pat);
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the abstract const by walking the thir and bailing out when
|
||||
/// encountering an unsupported operation.
|
||||
pub fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> {
|
||||
debug!("AbstractConstBuilder::build: body={:?}", &*self.body);
|
||||
self.recurse_build(self.body_id)?;
|
||||
|
||||
Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter()))
|
||||
}
|
||||
|
||||
fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> {
|
||||
use thir::ExprKind;
|
||||
let node = &self.body.exprs[node];
|
||||
Ok(match &node.kind {
|
||||
// I dont know if handling of these 3 is correct
|
||||
&ExprKind::Scope { value, .. } => self.recurse_build(value)?,
|
||||
&ExprKind::PlaceTypeAscription { source, .. }
|
||||
| &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?,
|
||||
&ExprKind::Literal { lit, neg } => {
|
||||
let sp = node.span;
|
||||
let constant = match self.tcx.at(sp).lit_to_const(LitToConstInput {
|
||||
lit: &lit.node,
|
||||
ty: node.ty,
|
||||
neg,
|
||||
}) {
|
||||
Ok(c) => c,
|
||||
Err(LitToConstError::Reported(guar)) => {
|
||||
self.tcx.const_error_with_guaranteed(node.ty, guar)
|
||||
}
|
||||
Err(LitToConstError::TypeError) => {
|
||||
bug!("encountered type error in lit_to_const")
|
||||
}
|
||||
};
|
||||
|
||||
self.nodes.push(Node::Leaf(constant))
|
||||
}
|
||||
&ExprKind::NonHirLiteral { lit, user_ty: _ } => {
|
||||
let val = ty::ValTree::from_scalar_int(lit);
|
||||
self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
|
||||
}
|
||||
&ExprKind::ZstLiteral { user_ty: _ } => {
|
||||
let val = ty::ValTree::zst();
|
||||
self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
|
||||
}
|
||||
&ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
|
||||
let uneval =
|
||||
ty::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs);
|
||||
|
||||
let constant = self.tcx.mk_const(ty::ConstKind::Unevaluated(uneval), node.ty);
|
||||
|
||||
self.nodes.push(Node::Leaf(constant))
|
||||
}
|
||||
|
||||
ExprKind::ConstParam { param, .. } => {
|
||||
let const_param = self.tcx.mk_const(ty::ConstKind::Param(*param), node.ty);
|
||||
self.nodes.push(Node::Leaf(const_param))
|
||||
}
|
||||
|
||||
ExprKind::Call { fun, args, .. } => {
|
||||
let fun = self.recurse_build(*fun)?;
|
||||
|
||||
let mut new_args = Vec::<NodeId>::with_capacity(args.len());
|
||||
for &id in args.iter() {
|
||||
new_args.push(self.recurse_build(id)?);
|
||||
}
|
||||
let new_args = self.tcx.arena.alloc_slice(&new_args);
|
||||
self.nodes.push(Node::FunctionCall(fun, new_args))
|
||||
}
|
||||
&ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => {
|
||||
let lhs = self.recurse_build(lhs)?;
|
||||
let rhs = self.recurse_build(rhs)?;
|
||||
self.nodes.push(Node::Binop(op, lhs, rhs))
|
||||
}
|
||||
&ExprKind::Unary { op, arg } if Self::check_unop(op) => {
|
||||
let arg = self.recurse_build(arg)?;
|
||||
self.nodes.push(Node::UnaryOp(op, arg))
|
||||
}
|
||||
// This is necessary so that the following compiles:
|
||||
//
|
||||
// ```
|
||||
// fn foo<const N: usize>(a: [(); N + 1]) {
|
||||
// bar::<{ N + 1 }>();
|
||||
// }
|
||||
// ```
|
||||
ExprKind::Block { block } => {
|
||||
if let thir::Block { stmts: box [], expr: Some(e), .. } = &self.body.blocks[*block]
|
||||
{
|
||||
self.recurse_build(*e)?
|
||||
} else {
|
||||
self.maybe_supported_error(GenericConstantTooComplexSub::BlockNotSupported(
|
||||
node.span,
|
||||
))?
|
||||
}
|
||||
}
|
||||
// `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a
|
||||
// "coercion cast" i.e. using a coercion or is a no-op.
|
||||
// This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
|
||||
&ExprKind::Use { source } => {
|
||||
let arg = self.recurse_build(source)?;
|
||||
self.nodes.push(Node::Cast(CastKind::Use, arg, node.ty))
|
||||
}
|
||||
&ExprKind::Cast { source } => {
|
||||
let arg = self.recurse_build(source)?;
|
||||
self.nodes.push(Node::Cast(CastKind::As, arg, node.ty))
|
||||
}
|
||||
ExprKind::Borrow { arg, .. } => {
|
||||
let arg_node = &self.body.exprs[*arg];
|
||||
|
||||
// Skip reborrows for now until we allow Deref/Borrow/AddressOf
|
||||
// expressions.
|
||||
// FIXME(generic_const_exprs): Verify/explain why this is sound
|
||||
if let ExprKind::Deref { arg } = arg_node.kind {
|
||||
self.recurse_build(arg)?
|
||||
} else {
|
||||
self.maybe_supported_error(GenericConstantTooComplexSub::BorrowNotSupported(
|
||||
node.span,
|
||||
))?
|
||||
}
|
||||
}
|
||||
// FIXME(generic_const_exprs): We may want to support these.
|
||||
ExprKind::AddressOf { .. } | ExprKind::Deref { .. } => self.maybe_supported_error(
|
||||
GenericConstantTooComplexSub::AddressAndDerefNotSupported(node.span),
|
||||
)?,
|
||||
ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error(
|
||||
GenericConstantTooComplexSub::ArrayNotSupported(node.span),
|
||||
)?,
|
||||
ExprKind::NeverToAny { .. } => self.maybe_supported_error(
|
||||
GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span),
|
||||
)?,
|
||||
ExprKind::Tuple { .. } => self.maybe_supported_error(
|
||||
GenericConstantTooComplexSub::TupleNotSupported(node.span),
|
||||
)?,
|
||||
ExprKind::Index { .. } => self.maybe_supported_error(
|
||||
GenericConstantTooComplexSub::IndexNotSupported(node.span),
|
||||
)?,
|
||||
ExprKind::Field { .. } => self.maybe_supported_error(
|
||||
GenericConstantTooComplexSub::FieldNotSupported(node.span),
|
||||
)?,
|
||||
ExprKind::ConstBlock { .. } => self.maybe_supported_error(
|
||||
GenericConstantTooComplexSub::ConstBlockNotSupported(node.span),
|
||||
)?,
|
||||
ExprKind::Adt(_) => self
|
||||
.maybe_supported_error(GenericConstantTooComplexSub::AdtNotSupported(node.span))?,
|
||||
// dont know if this is correct
|
||||
ExprKind::Pointer { .. } => {
|
||||
self.error(GenericConstantTooComplexSub::PointerNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Yield { .. } => {
|
||||
self.error(GenericConstantTooComplexSub::YieldNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => {
|
||||
self.error(GenericConstantTooComplexSub::LoopNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Box { .. } => {
|
||||
self.error(GenericConstantTooComplexSub::BoxNotSupported(node.span))?
|
||||
}
|
||||
|
||||
ExprKind::Unary { .. } => unreachable!(),
|
||||
// we handle valid unary/binary ops above
|
||||
ExprKind::Binary { .. } => {
|
||||
self.error(GenericConstantTooComplexSub::BinaryNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::LogicalOp { .. } => {
|
||||
self.error(GenericConstantTooComplexSub::LogicalOpNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
|
||||
self.error(GenericConstantTooComplexSub::AssignNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::Closure { .. } | ExprKind::Return { .. } => {
|
||||
self.error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))?
|
||||
}
|
||||
// let expressions imply control flow
|
||||
ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => {
|
||||
self.error(GenericConstantTooComplexSub::ControlFlowNotSupported(node.span))?
|
||||
}
|
||||
ExprKind::InlineAsm { .. } => {
|
||||
self.error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))?
|
||||
}
|
||||
|
||||
// we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
|
||||
ExprKind::VarRef { .. }
|
||||
| ExprKind::UpvarRef { .. }
|
||||
| ExprKind::StaticRef { .. }
|
||||
| ExprKind::ThreadLocalRef(_) => {
|
||||
self.error(GenericConstantTooComplexSub::OperationNotSupported(node.span))?
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
|
||||
pub fn thir_abstract_const<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def: ty::WithOptConstParam<LocalDefId>,
|
||||
) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
|
||||
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
|
||||
if tcx.features().generic_const_exprs {
|
||||
match tcx.def_kind(def.did) {
|
||||
// FIXME(generic_const_exprs): We currently only do this for anonymous constants,
|
||||
@ -416,10 +369,17 @@ pub fn thir_abstract_const<'tcx>(
|
||||
}
|
||||
|
||||
let body = tcx.thir_body(def)?;
|
||||
let (body, body_id) = (&*body.0.borrow(), body.1);
|
||||
|
||||
AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))?
|
||||
.map(AbstractConstBuilder::build)
|
||||
.transpose()
|
||||
let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body };
|
||||
visit::walk_expr(&mut is_poly_vis, &body[body_id]);
|
||||
if !is_poly_vis.is_poly {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let root_span = body.exprs[body_id].span;
|
||||
|
||||
Some(recurse_build(tcx, body, body_id, root_span)).transpose()
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
// check-pass
|
||||
#![feature(generic_const_exprs)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
const ASSOC: usize;
|
||||
}
|
||||
impl<T> Trait for T {
|
||||
const ASSOC: usize = std::mem::size_of::<T>();
|
||||
}
|
||||
|
||||
struct Foo<T: Trait>([u8; T::ASSOC])
|
||||
where
|
||||
[(); T::ASSOC]:;
|
||||
|
||||
fn bar<T: Trait>()
|
||||
where
|
||||
[(); T::ASSOC]:,
|
||||
{
|
||||
let _: Foo<T> = Foo::<_>(make());
|
||||
}
|
||||
|
||||
fn make() -> ! {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,15 @@
|
||||
#![feature(generic_const_exprs)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
const ASSOC: usize;
|
||||
}
|
||||
|
||||
fn foo<T: Trait, U: Trait>() where [(); U::ASSOC]:, {
|
||||
bar::<{ T::ASSOC }>();
|
||||
//~^ ERROR: unconstrained generic constant
|
||||
}
|
||||
|
||||
fn bar<const N: usize>() {}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,10 @@
|
||||
error: unconstrained generic constant
|
||||
--> $DIR/doesnt_unify_evaluatable.rs:9:11
|
||||
|
|
||||
LL | bar::<{ T::ASSOC }>();
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: try adding a `where` bound using this expression: `where [(); { T::ASSOC }]:`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,20 @@
|
||||
// check-pass
|
||||
#![feature(generic_const_exprs)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
const ASSOC: usize;
|
||||
}
|
||||
|
||||
struct Foo<T: Trait>(T)
|
||||
where
|
||||
[(); T::ASSOC]:;
|
||||
|
||||
impl<T: Trait> Drop for Foo<T>
|
||||
where
|
||||
[(); T::ASSOC]:,
|
||||
{
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,18 @@
|
||||
// check-pass
|
||||
|
||||
#![feature(generic_const_exprs)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
const ASSOC: usize;
|
||||
}
|
||||
|
||||
fn foo<T: Trait, U: Trait>() where [(); T::ASSOC]:, {
|
||||
bar::<{ T::ASSOC }>();
|
||||
}
|
||||
|
||||
fn bar<const N: usize>() -> [(); N] {
|
||||
[(); N]
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -24,7 +24,8 @@ where
|
||||
fn covariant(
|
||||
v: &'static Foo<for<'a> fn(&'a ())>
|
||||
) -> &'static Foo<fn(&'static ())> {
|
||||
v //~ ERROR mismatched types
|
||||
v
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -1,15 +1,15 @@
|
||||
error[E0391]: cycle detected when resolving instance `<LazyUpdim<'_, T, { T::DIM }, DIM> as TensorDimension>::DIM`
|
||||
error[E0391]: cycle detected when resolving instance `<LazyUpdim<'_, T, <T as TensorDimension>::DIM, DIM> as TensorDimension>::DIM`
|
||||
--> $DIR/issue-83765.rs:5:5
|
||||
|
|
||||
LL | const DIM: usize;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: ...which requires computing candidate for `<LazyUpdim<'_, T, { T::DIM }, DIM> as TensorDimension>`...
|
||||
note: ...which requires computing candidate for `<LazyUpdim<'_, T, <T as TensorDimension>::DIM, DIM> as TensorDimension>`...
|
||||
--> $DIR/issue-83765.rs:4:1
|
||||
|
|
||||
LL | trait TensorDimension {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: ...which again requires resolving instance `<LazyUpdim<'_, T, { T::DIM }, DIM> as TensorDimension>::DIM`, completing the cycle
|
||||
= note: ...which again requires resolving instance `<LazyUpdim<'_, T, <T as TensorDimension>::DIM, DIM> as TensorDimension>::DIM`, completing the cycle
|
||||
note: cycle used when computing candidate for `<LazyUpdim<'_, T, { T::DIM }, DIM> as TensorDimension>`
|
||||
--> $DIR/issue-83765.rs:4:1
|
||||
|
|
||||
|
@ -1,4 +1,8 @@
|
||||
// revisions: cfail
|
||||
// check-pass
|
||||
// known-bug
|
||||
|
||||
// This should not compile, as the compiler should not know
|
||||
// `A - 0` is satisfied `?x - 0` if `?x` is inferred to `A`.
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(generic_const_exprs)]
|
||||
|
||||
@ -6,8 +10,8 @@ pub struct Ref<'a>(&'a i32);
|
||||
|
||||
impl<'a> Ref<'a> {
|
||||
pub fn foo<const A: usize>() -> [(); A - 0] {
|
||||
//~^ WARN function cannot
|
||||
Self::foo()
|
||||
//~^ error: type annotations needed
|
||||
}
|
||||
}
|
||||
|
14
src/test/ui/const-generics/issues/issue-85031-2.stderr
Normal file
14
src/test/ui/const-generics/issues/issue-85031-2.stderr
Normal file
@ -0,0 +1,14 @@
|
||||
warning: function cannot return without recursing
|
||||
--> $DIR/issue-85031-2.rs:12:5
|
||||
|
|
||||
LL | pub fn foo<const A: usize>() -> [(); A - 0] {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
|
||||
LL |
|
||||
LL | Self::foo()
|
||||
| ----------- recursive call site
|
||||
|
|
||||
= help: a `loop` may express intention better if this is on purpose
|
||||
= note: `#[warn(unconditional_recursion)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
Loading…
Reference in New Issue
Block a user