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
40 changed files with 812 additions and 822 deletions
|
@ -561,6 +561,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => {
|
ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => {
|
||||||
throw_inval!(TooGeneric)
|
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) => {
|
ty::ConstKind::Error(reported) => {
|
||||||
throw_inval!(AlreadyReported(reported))
|
throw_inval!(AlreadyReported(reported))
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,6 +248,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
|
||||||
ty::ConstKind::Param(_)
|
ty::ConstKind::Param(_)
|
||||||
| ty::ConstKind::Value(_)
|
| ty::ConstKind::Value(_)
|
||||||
| ty::ConstKind::Unevaluated(..)
|
| ty::ConstKind::Unevaluated(..)
|
||||||
|
| ty::ConstKind::Expr(..)
|
||||||
| ty::ConstKind::Error(_) => ct.super_fold_with(self),
|
| 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::interpret::{ErrorHandled, EvalToValTreeResult};
|
||||||
use rustc_middle::mir::ConstraintCategory;
|
use rustc_middle::mir::ConstraintCategory;
|
||||||
use rustc_middle::traits::select;
|
use rustc_middle::traits::select;
|
||||||
use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind};
|
|
||||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||||
use rustc_middle::ty::fold::BoundVarReplacerDelegate;
|
use rustc_middle::ty::fold::BoundVarReplacerDelegate;
|
||||||
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
|
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 }
|
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 {
|
pub fn is_in_snapshot(&self) -> bool {
|
||||||
self.in_snapshot.get()
|
self.in_snapshot.get()
|
||||||
}
|
}
|
||||||
|
@ -1646,26 +1619,25 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||||
|
|
||||||
// Postpone the evaluation of constants whose substs depend on inference
|
// Postpone the evaluation of constants whose substs depend on inference
|
||||||
// variables
|
// variables
|
||||||
|
let tcx = self.tcx;
|
||||||
if substs.has_non_region_infer() {
|
if substs.has_non_region_infer() {
|
||||||
let ac = AbstractConst::new(self.tcx, unevaluated);
|
if let Some(ct) = tcx.bound_abstract_const(unevaluated.def)? {
|
||||||
match ac {
|
let ct = tcx.expand_abstract_consts(ct.subst(tcx, substs));
|
||||||
Ok(None) => {
|
if let Err(e) = ct.error_reported() {
|
||||||
substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did);
|
return Err(ErrorHandled::Reported(e));
|
||||||
param_env = self.tcx.param_env(unevaluated.def.did);
|
} 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)) => {
|
} else {
|
||||||
if ct.unify_failure_kind(self.tcx) == FailureKind::Concrete {
|
substs = InternalSubsts::identity_for_item(tcx, unevaluated.def.did);
|
||||||
substs = replace_param_and_infer_substs_with_placeholder(self.tcx, substs);
|
param_env = tcx.param_env(unevaluated.def.did);
|
||||||
} else {
|
|
||||||
return Err(ErrorHandled::TooGeneric);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(guar) => return Err(ErrorHandled::Reported(guar)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let param_env_erased = self.tcx.erase_regions(param_env);
|
let param_env_erased = tcx.erase_regions(param_env);
|
||||||
let substs_erased = self.tcx.erase_regions(substs);
|
let substs_erased = tcx.erase_regions(substs);
|
||||||
debug!(?param_env_erased);
|
debug!(?param_env_erased);
|
||||||
debug!(?substs_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
|
// 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.
|
// 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:
|
/// `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)] {
|
impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] {
|
||||||
fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self {
|
fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self {
|
||||||
ty::codec::RefDecodable::decode(d)
|
ty::codec::RefDecodable::decode(d)
|
||||||
|
|
|
@ -366,7 +366,7 @@ define_tables! {
|
||||||
mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>,
|
mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>,
|
||||||
promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>,
|
promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>,
|
||||||
// FIXME(compiler-errors): Why isn't this a LazyArray?
|
// 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_parent: Table<DefIndex, RawDefId>,
|
||||||
impl_polarity: Table<DefIndex, ty::ImplPolarity>,
|
impl_polarity: Table<DefIndex, ty::ImplPolarity>,
|
||||||
constness: Table<DefIndex, hir::Constness>,
|
constness: Table<DefIndex, hir::Constness>,
|
||||||
|
|
|
@ -476,6 +476,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
|
||||||
// These variants shouldn't exist in the MIR.
|
// These variants shouldn't exist in the MIR.
|
||||||
ty::ConstKind::Placeholder(_)
|
ty::ConstKind::Placeholder(_)
|
||||||
| ty::ConstKind::Infer(_)
|
| ty::ConstKind::Infer(_)
|
||||||
|
| ty::ConstKind::Expr(_)
|
||||||
| ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", literal),
|
| ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", literal),
|
||||||
},
|
},
|
||||||
ConstantKind::Unevaluated(uv, _) => {
|
ConstantKind::Unevaluated(uv, _) => {
|
||||||
|
|
|
@ -1185,7 +1185,8 @@ pub enum NullOp {
|
||||||
AlignOf,
|
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 {
|
pub enum UnOp {
|
||||||
/// The `!` operator for logical inversion
|
/// The `!` operator for logical inversion
|
||||||
Not,
|
Not,
|
||||||
|
@ -1193,7 +1194,8 @@ pub enum UnOp {
|
||||||
Neg,
|
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 {
|
pub enum BinOp {
|
||||||
/// The `+` operator (addition)
|
/// The `+` operator (addition)
|
||||||
Add,
|
Add,
|
||||||
|
|
|
@ -16,9 +16,7 @@ TrivialTypeTraversalAndLiftImpls! {
|
||||||
UserTypeAnnotationIndex,
|
UserTypeAnnotationIndex,
|
||||||
BorrowKind,
|
BorrowKind,
|
||||||
CastKind,
|
CastKind,
|
||||||
BinOp,
|
|
||||||
NullOp,
|
NullOp,
|
||||||
UnOp,
|
|
||||||
hir::Movability,
|
hir::Movability,
|
||||||
BasicBlock,
|
BasicBlock,
|
||||||
SwitchTargets,
|
SwitchTargets,
|
||||||
|
|
|
@ -400,7 +400,7 @@ rustc_queries! {
|
||||||
/// Try to build an abstract representation of the given constant.
|
/// Try to build an abstract representation of the given constant.
|
||||||
query thir_abstract_const(
|
query thir_abstract_const(
|
||||||
key: DefId
|
key: DefId
|
||||||
) -> Result<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> {
|
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
|
||||||
desc {
|
desc {
|
||||||
|tcx| "building an abstract representation for `{}`", tcx.def_path_str(key),
|
|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.
|
/// Try to build an abstract representation of the given constant.
|
||||||
query thir_abstract_const_of_const_arg(
|
query thir_abstract_const_of_const_arg(
|
||||||
key: (LocalDefId, DefId)
|
key: (LocalDefId, DefId)
|
||||||
) -> Result<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> {
|
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
|
||||||
desc {
|
desc {
|
||||||
|tcx|
|
|tcx|
|
||||||
"building an abstract representation for the const argument `{}`",
|
"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(
|
query mir_drops_elaborated_and_const_checked(
|
||||||
key: ty::WithOptConstParam<LocalDefId>
|
key: ty::WithOptConstParam<LocalDefId>
|
||||||
) -> &'tcx Steal<mir::Body<'tcx>> {
|
) -> &'tcx Steal<mir::Body<'tcx>> {
|
||||||
|
|
|
@ -1,98 +1,13 @@
|
||||||
//! A subset of a mir body used for const evaluatability checking.
|
//! A subset of a mir body used for const evaluatability checking.
|
||||||
use crate::mir;
|
use crate::ty::{
|
||||||
use crate::ty::visit::TypeVisitable;
|
self, Const, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
|
||||||
use crate::ty::{self, EarlyBinder, SubstsRef, Ty, TyCtxt};
|
TypeVisitable,
|
||||||
|
};
|
||||||
use rustc_errors::ErrorGuaranteed;
|
use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use std::cmp;
|
|
||||||
use std::ops::ControlFlow;
|
|
||||||
|
|
||||||
rustc_index::newtype_index! {
|
#[derive(Hash, Debug, Clone, Copy, Ord, PartialOrd, PartialEq, Eq)]
|
||||||
/// An index into an `AbstractConst`.
|
#[derive(TyDecodable, TyEncodable, HashStable, TypeVisitable, TypeFoldable)]
|
||||||
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)]
|
|
||||||
pub enum CastKind {
|
pub enum CastKind {
|
||||||
/// thir::ExprKind::As
|
/// thir::ExprKind::As
|
||||||
As,
|
As,
|
||||||
|
@ -100,16 +15,6 @@ pub enum CastKind {
|
||||||
Use,
|
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)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
|
||||||
pub enum NotConstEvaluatable {
|
pub enum NotConstEvaluatable {
|
||||||
Error(ErrorGuaranteed),
|
Error(ErrorGuaranteed),
|
||||||
|
@ -127,68 +32,53 @@ TrivialTypeTraversalAndLiftImpls! {
|
||||||
NotConstEvaluatable,
|
NotConstEvaluatable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>, ErrorGuaranteed>;
|
||||||
|
|
||||||
impl<'tcx> TyCtxt<'tcx> {
|
impl<'tcx> TyCtxt<'tcx> {
|
||||||
#[inline]
|
/// Returns a const without substs applied
|
||||||
pub fn thir_abstract_const_opt_const_arg(
|
pub fn bound_abstract_const(
|
||||||
self,
|
self,
|
||||||
def: ty::WithOptConstParam<DefId>,
|
uv: ty::WithOptConstParam<DefId>,
|
||||||
) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
|
) -> BoundAbstractConst<'tcx> {
|
||||||
if let Some((did, param_did)) = def.as_const_arg() {
|
let ac = if let Some((did, param_did)) = uv.as_const_arg() {
|
||||||
self.thir_abstract_const_of_const_arg((did, param_did))
|
self.thir_abstract_const_of_const_arg((did, param_did))
|
||||||
} else {
|
} else {
|
||||||
self.thir_abstract_const(def.did)
|
self.thir_abstract_const(uv.did)
|
||||||
}
|
};
|
||||||
}
|
Ok(ac?.map(|ac| EarlyBinder(ac)))
|
||||||
}
|
|
||||||
|
|
||||||
#[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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
impl<'tcx> TypeFolder<'tcx> for Expander<'tcx> {
|
||||||
// a constant found in the caller bounds, there are
|
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||||
// now three possible cases here.
|
self.tcx
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
}
|
||||||
pub enum FailureKind {
|
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||||
/// The abstract const still references an inference
|
if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) {
|
||||||
/// variable, in this case we return `TooGeneric`.
|
ty.super_fold_with(self)
|
||||||
MentionsInfer,
|
} else {
|
||||||
/// The abstract const references a generic parameter,
|
ty
|
||||||
/// this means that we emit an error here.
|
}
|
||||||
MentionsParam,
|
}
|
||||||
/// The substs are concrete enough that we can simply
|
fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> {
|
||||||
/// try and evaluate the given constant.
|
let ct = match c.kind() {
|
||||||
Concrete,
|
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>
|
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
|
||||||
for ty::List<ty::BoundVariableKind>
|
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! {
|
impl_decodable_via_ref! {
|
||||||
&'tcx ty::TypeckResults<'tcx>,
|
&'tcx ty::TypeckResults<'tcx>,
|
||||||
&'tcx ty::List<Ty<'tcx>>,
|
&'tcx ty::List<Ty<'tcx>>,
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use super::Const;
|
||||||
use crate::mir;
|
use crate::mir;
|
||||||
use crate::mir::interpret::{AllocId, ConstValue, Scalar};
|
use crate::mir::interpret::{AllocId, ConstValue, Scalar};
|
||||||
|
use crate::ty::abstract_const::CastKind;
|
||||||
use crate::ty::subst::{InternalSubsts, SubstsRef};
|
use crate::ty::subst::{InternalSubsts, SubstsRef};
|
||||||
use crate::ty::ParamEnv;
|
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_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||||
use rustc_errors::ErrorGuaranteed;
|
use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_hir::def_id::DefId;
|
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
|
/// A placeholder for a const which could not be computed; this is
|
||||||
/// propagated to avoid useless error messages.
|
/// propagated to avoid useless error messages.
|
||||||
Error(ErrorGuaranteed),
|
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"))]
|
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
|
||||||
static_assert_size!(ConstKind<'_>, 32);
|
static_assert_size!(ConstKind<'_>, 32);
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,7 @@ pub struct CtxtInterners<'tcx> {
|
||||||
// Specifically use a speedy hash algorithm for these hash sets, since
|
// Specifically use a speedy hash algorithm for these hash sets, since
|
||||||
// they're accessed quite often.
|
// they're accessed quite often.
|
||||||
type_: InternedSet<'tcx, WithStableHash<TyS<'tcx>>>,
|
type_: InternedSet<'tcx, WithStableHash<TyS<'tcx>>>,
|
||||||
|
const_lists: InternedSet<'tcx, List<ty::Const<'tcx>>>,
|
||||||
substs: InternedSet<'tcx, InternalSubsts<'tcx>>,
|
substs: InternedSet<'tcx, InternalSubsts<'tcx>>,
|
||||||
canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo<'tcx>>>,
|
canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo<'tcx>>>,
|
||||||
region: InternedSet<'tcx, RegionKind<'tcx>>,
|
region: InternedSet<'tcx, RegionKind<'tcx>>,
|
||||||
|
@ -157,6 +158,7 @@ impl<'tcx> CtxtInterners<'tcx> {
|
||||||
CtxtInterners {
|
CtxtInterners {
|
||||||
arena,
|
arena,
|
||||||
type_: Default::default(),
|
type_: Default::default(),
|
||||||
|
const_lists: Default::default(),
|
||||||
substs: Default::default(),
|
substs: Default::default(),
|
||||||
region: Default::default(),
|
region: Default::default(),
|
||||||
poly_existential_predicates: Default::default(),
|
poly_existential_predicates: Default::default(),
|
||||||
|
@ -2261,6 +2263,7 @@ macro_rules! slice_interners {
|
||||||
}
|
}
|
||||||
|
|
||||||
slice_interners!(
|
slice_interners!(
|
||||||
|
const_lists: _intern_const_list(Const<'tcx>),
|
||||||
substs: _intern_substs(GenericArg<'tcx>),
|
substs: _intern_substs(GenericArg<'tcx>),
|
||||||
canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo<'tcx>),
|
canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo<'tcx>),
|
||||||
poly_existential_predicates:
|
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>> {
|
pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx List<Ty<'tcx>> {
|
||||||
if ts.is_empty() {
|
if ts.is_empty() {
|
||||||
List::empty()
|
List::empty()
|
||||||
|
|
|
@ -356,7 +356,10 @@ impl DeepRejectCtxt {
|
||||||
|
|
||||||
pub fn consts_may_unify(self, obligation_ct: ty::Const<'_>, impl_ct: ty::Const<'_>) -> bool {
|
pub fn consts_may_unify(self, obligation_ct: ty::Const<'_>, impl_ct: ty::Const<'_>) -> bool {
|
||||||
match impl_ct.kind() {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
ty::ConstKind::Value(_) => {}
|
ty::ConstKind::Value(_) => {}
|
||||||
|
@ -374,7 +377,9 @@ impl DeepRejectCtxt {
|
||||||
|
|
||||||
// As we don't necessarily eagerly evaluate constants,
|
// As we don't necessarily eagerly evaluate constants,
|
||||||
// they might unify with any value.
|
// 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(obl) => match k {
|
||||||
ty::ConstKind::Value(imp) => obl == imp,
|
ty::ConstKind::Value(imp) => obl == imp,
|
||||||
_ => true,
|
_ => true,
|
||||||
|
|
|
@ -313,6 +313,26 @@ impl FlagComputation {
|
||||||
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
|
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
|
||||||
}
|
}
|
||||||
ty::ConstKind::Value(_) => {}
|
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),
|
ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ pub use self::closure::{
|
||||||
CAPTURE_STRUCT_LOCAL,
|
CAPTURE_STRUCT_LOCAL,
|
||||||
};
|
};
|
||||||
pub use self::consts::{
|
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::{
|
pub use self::context::{
|
||||||
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
|
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
|
||||||
|
|
|
@ -4,7 +4,6 @@ use rustc_index::vec::{Idx, IndexVec};
|
||||||
|
|
||||||
use crate::middle::exported_symbols::ExportedSymbol;
|
use crate::middle::exported_symbols::ExportedSymbol;
|
||||||
use crate::mir::Body;
|
use crate::mir::Body;
|
||||||
use crate::ty::abstract_const::Node;
|
|
||||||
use crate::ty::{
|
use crate::ty::{
|
||||||
self, Const, FnSig, GeneratorDiagnosticData, GenericPredicates, Predicate, TraitRef, Ty,
|
self, Const, FnSig, GeneratorDiagnosticData, GenericPredicates, Predicate, TraitRef, Ty,
|
||||||
};
|
};
|
||||||
|
@ -124,6 +123,5 @@ parameterized_over_tcx! {
|
||||||
Predicate,
|
Predicate,
|
||||||
GeneratorDiagnosticData,
|
GeneratorDiagnosticData,
|
||||||
Body,
|
Body,
|
||||||
Node,
|
|
||||||
ExportedSymbol,
|
ExportedSymbol,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1253,6 +1253,9 @@ pub trait PrettyPrinter<'tcx>:
|
||||||
self.pretty_print_bound_var(debruijn, bound_var)?
|
self.pretty_print_bound_var(debruijn, bound_var)?
|
||||||
}
|
}
|
||||||
ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)),
|
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]"),
|
ty::ConstKind::Error(_) => p!("[const error]"),
|
||||||
};
|
};
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//! subtyping, type equality, etc.
|
//! subtyping, type equality, etc.
|
||||||
|
|
||||||
use crate::ty::error::{ExpectedFound, TypeError};
|
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 crate::ty::{GenericArg, GenericArgKind, SubstsRef};
|
||||||
use rustc_hir as ast;
|
use rustc_hir as ast;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
|
@ -613,7 +613,10 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
|
||||||
if a_ty != b_ty {
|
if a_ty != b_ty {
|
||||||
relation.tcx().sess.delay_span_bug(
|
relation.tcx().sess.delay_span_bug(
|
||||||
DUMMY_SP,
|
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.
|
// an unnormalized (i.e. unevaluated) const in the param-env.
|
||||||
// FIXME(generic_const_exprs): Once we always lazily unify unevaluated constants
|
// FIXME(generic_const_exprs): Once we always lazily unify unevaluated constants
|
||||||
// these `eval` calls can be removed.
|
// 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());
|
a = a.eval(tcx, relation.param_env());
|
||||||
b = b.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,
|
// Currently, the values that can be unified are primitive types,
|
||||||
// and those that derive both `PartialEq` and `Eq`, corresponding
|
// and those that derive both `PartialEq` and `Eq`, corresponding
|
||||||
// to structural-match types.
|
// 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::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
|
||||||
(ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
|
(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`
|
// 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
|
// and is the better alternative to waiting until `generic_const_exprs` can
|
||||||
// be stabilized.
|
// be stabilized.
|
||||||
(ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if au.def == bu.def => {
|
(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(
|
let substs = relation.relate_with_variance(
|
||||||
ty::Variance::Invariant,
|
ty::Variance::Invariant,
|
||||||
ty::VarianceDiagInfo::default(),
|
ty::VarianceDiagInfo::default(),
|
||||||
|
@ -665,6 +668,50 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
|
||||||
a.ty(),
|
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,
|
_ => false,
|
||||||
};
|
};
|
||||||
if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(expected_found(relation, a, b))) }
|
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> {
|
impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ProjectionKind> {
|
||||||
fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
|
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))
|
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::Value(_)
|
||||||
| ty::ConstKind::Error(_) => {}
|
| 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) => {
|
ty::ConstKind::Unevaluated(ct) => {
|
||||||
stack.extend(ct.substs.iter().rev());
|
stack.extend(ct.substs.iter().rev());
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#![feature(control_flow_enum)]
|
#![feature(control_flow_enum)]
|
||||||
#![feature(rustc_private)]
|
#![feature(rustc_private)]
|
||||||
#![feature(try_blocks)]
|
#![feature(try_blocks)]
|
||||||
|
#![feature(let_chains)]
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
#![deny(rustc::untranslatable_diagnostic)]
|
#![deny(rustc::untranslatable_diagnostic)]
|
||||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||||
|
@ -25,7 +26,6 @@ use rustc_middle::bug;
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_middle::middle::privacy::{EffectiveVisibilities, Level};
|
use rustc_middle::middle::privacy::{EffectiveVisibilities, Level};
|
||||||
use rustc_middle::span_bug;
|
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::query::Providers;
|
||||||
use rustc_middle::ty::subst::InternalSubsts;
|
use rustc_middle::ty::subst::InternalSubsts;
|
||||||
use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind};
|
use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind};
|
||||||
|
@ -288,19 +288,8 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||||
self.visit_ty(c.ty())?;
|
|
||||||
let tcx = self.def_id_visitor.tcx();
|
let tcx = self.def_id_visitor.tcx();
|
||||||
if let Ok(Some(ct)) = AbstractConst::from_const(tcx, c) {
|
tcx.expand_abstract_consts(c).super_visit_with(self)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)] {
|
impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] {
|
||||||
fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
|
fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
|
||||||
RefDecodable::decode(d)
|
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
|
// 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.
|
// the path could refer back to e.g. an `impl` using the constant.
|
||||||
ty::ConstKind::Unevaluated(_)
|
ty::ConstKind::Unevaluated(_)
|
||||||
|
| ty::ConstKind::Expr(_)
|
||||||
| ty::ConstKind::Param(_)
|
| ty::ConstKind::Param(_)
|
||||||
| ty::ConstKind::Infer(_)
|
| ty::ConstKind::Infer(_)
|
||||||
| ty::ConstKind::Bound(..)
|
| ty::ConstKind::Bound(..)
|
||||||
|
|
|
@ -8,152 +8,18 @@
|
||||||
//! In this case we try to build an abstract representation of this constant using
|
//! 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
|
//! `thir_abstract_const` which can then be checked for structural equality with other
|
||||||
//! generic constants mentioned in the `caller_bounds` of the current environment.
|
//! 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_infer::infer::InferCtxt;
|
||||||
use rustc_middle::mir::interpret::ErrorHandled;
|
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;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
pub struct ConstUnifyCtxt<'tcx> {
|
use crate::traits::ObligationCtxt;
|
||||||
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`.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if a given constant can be evaluated.
|
/// Check if a given constant can be evaluated.
|
||||||
#[instrument(skip(infcx), level = "debug")]
|
#[instrument(skip(infcx), level = "debug")]
|
||||||
|
@ -166,6 +32,8 @@ pub fn is_const_evaluatable<'tcx>(
|
||||||
let tcx = infcx.tcx;
|
let tcx = infcx.tcx;
|
||||||
let uv = match ct.kind() {
|
let uv = match ct.kind() {
|
||||||
ty::ConstKind::Unevaluated(uv) => uv,
|
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::Param(_)
|
||||||
| ty::ConstKind::Bound(_, _)
|
| ty::ConstKind::Bound(_, _)
|
||||||
| ty::ConstKind::Placeholder(_)
|
| ty::ConstKind::Placeholder(_)
|
||||||
|
@ -175,21 +43,25 @@ pub fn is_const_evaluatable<'tcx>(
|
||||||
};
|
};
|
||||||
|
|
||||||
if tcx.features().generic_const_exprs {
|
if tcx.features().generic_const_exprs {
|
||||||
if let Some(ct) = AbstractConst::new(tcx, uv)? {
|
let ct = tcx.expand_abstract_consts(ct);
|
||||||
if satisfied_from_param_env(tcx, ct, param_env)? {
|
|
||||||
|
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(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
match ct.unify_failure_kind(tcx) {
|
if ct.has_non_region_infer() {
|
||||||
FailureKind::MentionsInfer => {
|
return Err(NotConstEvaluatable::MentionsInfer);
|
||||||
return Err(NotConstEvaluatable::MentionsInfer);
|
} else if ct.has_non_region_param() {
|
||||||
}
|
return Err(NotConstEvaluatable::MentionsParam);
|
||||||
FailureKind::MentionsParam => {
|
|
||||||
return Err(NotConstEvaluatable::MentionsParam);
|
|
||||||
}
|
|
||||||
// returned below
|
|
||||||
FailureKind::Concrete => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
|
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
|
||||||
match concrete {
|
match concrete {
|
||||||
Err(ErrorHandled::TooGeneric) => Err(NotConstEvaluatable::Error(
|
Err(ErrorHandled::TooGeneric) => Err(NotConstEvaluatable::Error(
|
||||||
|
@ -211,28 +83,33 @@ pub fn is_const_evaluatable<'tcx>(
|
||||||
//
|
//
|
||||||
// See #74595 for more details about this.
|
// See #74595 for more details about this.
|
||||||
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
|
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
|
||||||
|
|
||||||
match concrete {
|
match concrete {
|
||||||
// If we're evaluating a foreign constant, under a nightly compiler without generic
|
// If we're evaluating a generic foreign constant, under a nightly compiler while
|
||||||
// const exprs, AND it would've passed if that expression had been evaluated with
|
// the current crate does not enable `feature(generic_const_exprs)`, abort
|
||||||
// generic const exprs, then suggest using generic const exprs.
|
// compilation with a useful error.
|
||||||
Err(_) if tcx.sess.is_nightly_build()
|
Err(_)
|
||||||
&& let Ok(Some(ct)) = AbstractConst::new(tcx, uv)
|
if tcx.sess.is_nightly_build()
|
||||||
&& satisfied_from_param_env(tcx, ct, param_env) == Ok(true) => {
|
&& satisfied_from_param_env(
|
||||||
tcx.sess
|
tcx,
|
||||||
.struct_span_fatal(
|
infcx,
|
||||||
// Slightly better span than just using `span` alone
|
tcx.expand_abstract_consts(ct),
|
||||||
if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
|
param_env,
|
||||||
"failed to evaluate generic const expression",
|
) =>
|
||||||
)
|
{
|
||||||
.note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
|
tcx.sess
|
||||||
.span_suggestion_verbose(
|
.struct_span_fatal(
|
||||||
rustc_span::DUMMY_SP,
|
// Slightly better span than just using `span` alone
|
||||||
"consider enabling this feature",
|
if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
|
||||||
"#![feature(generic_const_exprs)]\n",
|
"failed to evaluate generic const expression",
|
||||||
rustc_errors::Applicability::MaybeIncorrect,
|
)
|
||||||
)
|
.note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
|
||||||
.emit()
|
.span_suggestion_verbose(
|
||||||
|
rustc_span::DUMMY_SP,
|
||||||
|
"consider enabling this feature",
|
||||||
|
"#![feature(generic_const_exprs)]\n",
|
||||||
|
rustc_errors::Applicability::MaybeIncorrect,
|
||||||
|
)
|
||||||
|
.emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(ErrorHandled::TooGeneric) => {
|
Err(ErrorHandled::TooGeneric) => {
|
||||||
|
@ -241,49 +118,82 @@ pub fn is_const_evaluatable<'tcx>(
|
||||||
} else if uv.has_non_region_param() {
|
} else if uv.has_non_region_param() {
|
||||||
NotConstEvaluatable::MentionsParam
|
NotConstEvaluatable::MentionsParam
|
||||||
} else {
|
} 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)
|
NotConstEvaluatable::Error(guar)
|
||||||
};
|
};
|
||||||
|
|
||||||
Err(err)
|
Err(err)
|
||||||
},
|
}
|
||||||
Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
|
Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(tcx), level = "debug")]
|
#[instrument(skip(infcx, tcx), level = "debug")]
|
||||||
fn satisfied_from_param_env<'tcx>(
|
fn satisfied_from_param_env<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
ct: AbstractConst<'tcx>,
|
infcx: &InferCtxt<'tcx>,
|
||||||
|
ct: ty::Const<'tcx>,
|
||||||
param_env: ty::ParamEnv<'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() {
|
for pred in param_env.caller_bounds() {
|
||||||
match pred.kind().skip_binder() {
|
match pred.kind().skip_binder() {
|
||||||
ty::PredicateKind::ConstEvaluatable(uv) => {
|
ty::PredicateKind::ConstEvaluatable(ce) => {
|
||||||
if let Some(b_ct) = AbstractConst::from_const(tcx, uv)? {
|
let b_ct = tcx.expand_abstract_consts(ce);
|
||||||
let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
|
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
|
if let ControlFlow::Break(()) = result {
|
||||||
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
|
debug!("is_const_evaluatable: abstract_const ~~> ok");
|
||||||
// predicate for `(N + 1) * 2`
|
return true;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {} // don't care
|
_ => {} // don't care
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(false)
|
false
|
||||||
}
|
}
|
||||||
|
|
|
@ -455,20 +455,47 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::PredicateKind::ConstEquate(c1, c2) => {
|
ty::PredicateKind::ConstEquate(c1, c2) => {
|
||||||
|
let tcx = self.selcx.tcx();
|
||||||
assert!(
|
assert!(
|
||||||
self.selcx.tcx().features().generic_const_exprs,
|
tcx.features().generic_const_exprs,
|
||||||
"`ConstEquate` without a feature gate: {c1:?} {c2:?}",
|
"`ConstEquate` without a feature gate: {c1:?} {c2:?}",
|
||||||
);
|
);
|
||||||
debug!(?c1, ?c2, "equating consts");
|
|
||||||
// FIXME: we probably should only try to unify abstract constants
|
// FIXME: we probably should only try to unify abstract constants
|
||||||
// if the constants depend on generic parameters.
|
// if the constants depend on generic parameters.
|
||||||
//
|
//
|
||||||
// Let's just see where this breaks :shrug:
|
// 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) {
|
let c1 = tcx.expand_abstract_consts(c1);
|
||||||
return ProcessResult::Changed(vec![]);
|
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)
|
.at(&obligation.cause, obligation.param_env)
|
||||||
.eq(c1, c2)
|
.eq(c1, c2)
|
||||||
{
|
{
|
||||||
Ok(_) => ProcessResult::Changed(vec![]),
|
Ok(inf_ok) => {
|
||||||
|
ProcessResult::Changed(mk_pending(inf_ok.into_obligations()))
|
||||||
|
}
|
||||||
Err(err) => ProcessResult::Error(
|
Err(err) => ProcessResult::Error(
|
||||||
FulfillmentErrorCode::CodeConstEquateError(
|
FulfillmentErrorCode::CodeConstEquateError(
|
||||||
ExpectedFound::new(true, c1, c2),
|
ExpectedFound::new(true, c1, c2),
|
||||||
|
|
|
@ -932,10 +932,6 @@ pub fn provide(providers: &mut ty::query::Providers) {
|
||||||
vtable_trait_upcasting_coercion_new_vptr_slot,
|
vtable_trait_upcasting_coercion_new_vptr_slot,
|
||||||
subst_and_check_impossible_predicates,
|
subst_and_check_impossible_predicates,
|
||||||
is_impossible_method,
|
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
|
..*providers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,10 @@ use hir::def::DefKind;
|
||||||
use rustc_errors::{DelayDm, FatalError, MultiSpan};
|
use rustc_errors::{DelayDm, FatalError, MultiSpan};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
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::{
|
use rustc_middle::ty::{
|
||||||
self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
|
self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{GenericArg, InternalSubsts};
|
|
||||||
use rustc_middle::ty::{Predicate, ToPredicate};
|
use rustc_middle::ty::{Predicate, ToPredicate};
|
||||||
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
|
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
|
||||||
use rustc_span::symbol::Symbol;
|
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> {
|
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.
|
// This is only possible for unevaluated constants, so we walk these here.
|
||||||
//
|
self.tcx.expand_abstract_consts(ct).super_visit_with(self)
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -657,21 +657,62 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::PredicateKind::ConstEquate(c1, c2) => {
|
ty::PredicateKind::ConstEquate(c1, c2) => {
|
||||||
|
let tcx = self.tcx();
|
||||||
assert!(
|
assert!(
|
||||||
self.tcx().features().generic_const_exprs,
|
tcx.features().generic_const_exprs,
|
||||||
"`ConstEquate` without a feature gate: {c1:?} {c2:?}",
|
"`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) {
|
let c1 = tcx.expand_abstract_consts(c1);
|
||||||
return Ok(EvaluatedToOk);
|
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)
|
.at(&obligation.cause, obligation.param_env)
|
||||||
.eq(c1, c2)
|
.eq(c1, c2)
|
||||||
{
|
{
|
||||||
Ok(_) => Ok(EvaluatedToOk),
|
Ok(inf_ok) => self.evaluate_predicates_recursively(
|
||||||
|
previous_stack,
|
||||||
|
inf_ok.into_obligations(),
|
||||||
|
),
|
||||||
Err(_) => Ok(EvaluatedToErr),
|
Err(_) => Ok(EvaluatedToErr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -476,6 +476,11 @@ impl<'tcx> WfPredicates<'tcx> {
|
||||||
ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())),
|
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::Error(_)
|
||||||
| ty::ConstKind::Param(_)
|
| ty::ConstKind::Param(_)
|
||||||
| ty::ConstKind::Bound(..)
|
| ty::ConstKind::Bound(..)
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use rustc_errors::ErrorGuaranteed;
|
use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_index::vec::IndexVec;
|
|
||||||
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
|
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
|
||||||
use rustc_middle::ty::abstract_const::{CastKind, Node, NodeId};
|
use rustc_middle::thir::visit;
|
||||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
|
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_middle::{mir, thir};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_target::abi::VariantIdx;
|
use rustc_target::abi::VariantIdx;
|
||||||
|
@ -76,334 +77,286 @@ pub(crate) fn destructure_const<'tcx>(
|
||||||
ty::DestructuredConst { variant, fields }
|
ty::DestructuredConst { variant, fields }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AbstractConstBuilder<'a, 'tcx> {
|
/// We do not allow all binary operations in abstract consts, so filter disallowed ones.
|
||||||
tcx: TyCtxt<'tcx>,
|
fn check_binop(op: mir::BinOp) -> bool {
|
||||||
body_id: thir::ExprId,
|
use mir::BinOp::*;
|
||||||
body: &'a thir::Thir<'tcx>,
|
match op {
|
||||||
/// The current WIP node tree.
|
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le | Ne
|
||||||
nodes: IndexVec<NodeId, Node<'tcx>>,
|
| Ge | Gt => true,
|
||||||
|
Offset => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
/// While we currently allow all unary operations, we still want to explicitly guard against
|
||||||
fn root_span(&self) -> Span {
|
/// future changes here.
|
||||||
self.body.exprs[self.body_id].span
|
fn check_unop(op: mir::UnOp) -> bool {
|
||||||
|
use mir::UnOp::*;
|
||||||
|
match op {
|
||||||
|
Not | Neg => true,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn error(&mut self, sub: GenericConstantTooComplexSub) -> Result<!, ErrorGuaranteed> {
|
fn recurse_build<'tcx>(
|
||||||
let reported = self.tcx.sess.emit_err(GenericConstantTooComplex {
|
tcx: TyCtxt<'tcx>,
|
||||||
span: self.root_span(),
|
body: &thir::Thir<'tcx>,
|
||||||
maybe_supported: None,
|
node: thir::ExprId,
|
||||||
sub,
|
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(
|
Ok(match &node.kind {
|
||||||
&mut self,
|
// I dont know if handling of these 3 is correct
|
||||||
sub: GenericConstantTooComplexSub,
|
&ExprKind::Scope { value, .. } => recurse_build(tcx, body, value, root_span)?,
|
||||||
) -> Result<!, ErrorGuaranteed> {
|
&ExprKind::PlaceTypeAscription { source, .. }
|
||||||
let reported = self.tcx.sess.emit_err(GenericConstantTooComplex {
|
| &ExprKind::ValueTypeAscription { source, .. } => {
|
||||||
span: self.root_span(),
|
recurse_build(tcx, body, source, 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>,
|
|
||||||
}
|
}
|
||||||
|
&ExprKind::Literal { lit, neg } => {
|
||||||
use crate::rustc_middle::thir::visit::Visitor;
|
let sp = node.span;
|
||||||
use thir::visit;
|
match tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) {
|
||||||
|
Ok(c) => c,
|
||||||
impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
|
Err(LitToConstError::Reported(guar)) => {
|
||||||
fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool {
|
tcx.const_error_with_guaranteed(node.ty, guar)
|
||||||
if expr.ty.has_non_region_param() {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
Err(LitToConstError::TypeError) => {
|
||||||
match expr.kind {
|
bug!("encountered type error in lit_to_const")
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&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> {
|
ExprKind::Call { fun, args, .. } => {
|
||||||
fn thir(&self) -> &'a thir::Thir<'tcx> {
|
let fun = recurse_build(tcx, body, *fun, root_span)?;
|
||||||
&self.thir
|
|
||||||
|
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)?);
|
||||||
}
|
}
|
||||||
|
let new_args = tcx.mk_const_list(new_args.iter());
|
||||||
#[instrument(skip(self), level = "debug")]
|
tcx.mk_const(ConstKind::Expr(Expr::FunctionCall(fun, new_args)), node.ty)
|
||||||
fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) {
|
}
|
||||||
self.is_poly |= self.expr_is_poly(expr);
|
&ExprKind::Binary { op, lhs, rhs } if check_binop(op) => {
|
||||||
if !self.is_poly {
|
let lhs = recurse_build(tcx, body, lhs, root_span)?;
|
||||||
visit::walk_expr(self, expr)
|
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) => {
|
||||||
#[instrument(skip(self), level = "debug")]
|
let arg = recurse_build(tcx, body, arg, root_span)?;
|
||||||
fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) {
|
tcx.mk_const(ConstKind::Expr(Expr::UnOp(op, arg)), node.ty)
|
||||||
self.is_poly |= self.pat_is_poly(pat);
|
}
|
||||||
if !self.is_poly {
|
// This is necessary so that the following compiles:
|
||||||
visit::walk_pat(self, pat);
|
//
|
||||||
}
|
// ```
|
||||||
|
// 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 };
|
// Skip reborrows for now until we allow Deref/Borrow/AddressOf
|
||||||
visit::walk_expr(&mut is_poly_vis, &body[body_id]);
|
// expressions.
|
||||||
debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly);
|
// FIXME(generic_const_exprs): Verify/explain why this is sound
|
||||||
if !is_poly_vis.is_poly {
|
if let ExprKind::Deref { arg } = arg_node.kind {
|
||||||
return Ok(None);
|
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.
|
#[instrument(skip(self), level = "debug")]
|
||||||
fn check_binop(op: mir::BinOp) -> bool {
|
fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) {
|
||||||
use mir::BinOp::*;
|
self.is_poly |= self.expr_is_poly(expr);
|
||||||
match op {
|
if !self.is_poly {
|
||||||
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le
|
visit::walk_expr(self, expr)
|
||||||
| Ne | Ge | Gt => true,
|
|
||||||
Offset => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// While we currently allow all unary operations, we still want to explicitly guard against
|
#[instrument(skip(self), level = "debug")]
|
||||||
/// future changes here.
|
fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) {
|
||||||
fn check_unop(op: mir::UnOp) -> bool {
|
self.is_poly |= self.pat_is_poly(pat);
|
||||||
use mir::UnOp::*;
|
if !self.is_poly {
|
||||||
match op {
|
visit::walk_pat(self, pat);
|
||||||
Not | Neg => true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
|
||||||
pub fn thir_abstract_const<'tcx>(
|
pub fn thir_abstract_const<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
def: ty::WithOptConstParam<LocalDefId>,
|
def: ty::WithOptConstParam<LocalDefId>,
|
||||||
) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
|
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
|
||||||
if tcx.features().generic_const_exprs {
|
if tcx.features().generic_const_exprs {
|
||||||
match tcx.def_kind(def.did) {
|
match tcx.def_kind(def.did) {
|
||||||
// FIXME(generic_const_exprs): We currently only do this for anonymous constants,
|
// 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 = tcx.thir_body(def)?;
|
||||||
|
let (body, body_id) = (&*body.0.borrow(), body.1);
|
||||||
|
|
||||||
AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))?
|
let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body };
|
||||||
.map(AbstractConstBuilder::build)
|
visit::walk_expr(&mut is_poly_vis, &body[body_id]);
|
||||||
.transpose()
|
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 {
|
} else {
|
||||||
Ok(None)
|
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(
|
fn covariant(
|
||||||
v: &'static Foo<for<'a> fn(&'a ())>
|
v: &'static Foo<for<'a> fn(&'a ())>
|
||||||
) -> &'static Foo<fn(&'static ())> {
|
) -> &'static Foo<fn(&'static ())> {
|
||||||
v //~ ERROR mismatched types
|
v
|
||||||
|
//~^ ERROR mismatched types
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
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
|
--> $DIR/issue-83765.rs:5:5
|
||||||
|
|
|
|
||||||
LL | const DIM: usize;
|
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
|
--> $DIR/issue-83765.rs:4:1
|
||||||
|
|
|
|
||||||
LL | trait TensorDimension {
|
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>`
|
note: cycle used when computing candidate for `<LazyUpdim<'_, T, { T::DIM }, DIM> as TensorDimension>`
|
||||||
--> $DIR/issue-83765.rs:4:1
|
--> $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)]
|
#![allow(incomplete_features)]
|
||||||
#![feature(generic_const_exprs)]
|
#![feature(generic_const_exprs)]
|
||||||
|
|
||||||
|
@ -6,8 +10,8 @@ pub struct Ref<'a>(&'a i32);
|
||||||
|
|
||||||
impl<'a> Ref<'a> {
|
impl<'a> Ref<'a> {
|
||||||
pub fn foo<const A: usize>() -> [(); A - 0] {
|
pub fn foo<const A: usize>() -> [(); A - 0] {
|
||||||
|
//~^ WARN function cannot
|
||||||
Self::foo()
|
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…
Add table
Add a link
Reference in a new issue