Add empty ConstKind::Abstract

Initial pass at expr/abstract const/s

Address comments

Switch to using a list instead of &[ty::Const], rm `AbstractConst`

Remove try_unify_abstract_consts

Update comments

Add edits

Recurse more

More edits

Prevent equating associated consts

Move failing test to ui

Changes this test from incremental to ui, and mark it as failing and a known bug.
Does not cause the compiler to ICE, so should be ok.
This commit is contained in:
kadmin 2022-07-27 07:27:52 +00:00
parent 41e0363055
commit f9750c1554
40 changed files with 851 additions and 792 deletions

View File

@ -561,6 +561,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => {
throw_inval!(TooGeneric)
}
ty::ConstKind::Expr(_) => throw_inval!(TooGeneric),
ty::ConstKind::Error(reported) => {
throw_inval!(AlreadyReported(reported))
}

View File

@ -193,6 +193,18 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
ty::PredicateKind::ConstEvaluatable(a),
ty::PredicateKind::ConstEvaluatable(b),
) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(),
/*
) => {
if let (Ok(Some(a)), Ok(Some(b))) = (
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(a.def), a.substs),
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(b.def), b.substs),
) && a.ty() == b.ty() {
return relator.relate(a, b).is_ok();
} else {
false
}
}
*/
(
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)),
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)),

View File

@ -248,6 +248,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
ty::ConstKind::Param(_)
| ty::ConstKind::Value(_)
| ty::ConstKind::Unevaluated(..)
| ty::ConstKind::Expr(..)
| ty::ConstKind::Error(_) => ct.super_fold_with(self),
}
}

View File

@ -23,7 +23,6 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::select;
use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind};
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::BoundVarReplacerDelegate;
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
@ -713,32 +712,6 @@ impl<'tcx> InferCtxt<'tcx> {
TypeErrCtxt { infcx: self, typeck_results: None, fallback_has_occurred: false }
}
/// calls `tcx.try_unify_abstract_consts` after
/// canonicalizing the consts.
#[instrument(skip(self), level = "debug")]
pub fn try_unify_abstract_consts(
&self,
a: ty::UnevaluatedConst<'tcx>,
b: ty::UnevaluatedConst<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> bool {
// Reject any attempt to unify two unevaluated constants that contain inference
// variables, since inference variables in queries lead to ICEs.
if a.substs.has_non_region_infer()
|| b.substs.has_non_region_infer()
|| param_env.has_non_region_infer()
{
debug!("a or b or param_env contain infer vars in its substs -> cannot unify");
return false;
}
let param_env_and = param_env.and((a, b));
let erased = self.tcx.erase_regions(param_env_and);
debug!("after erase_regions: {:?}", erased);
self.tcx.try_unify_abstract_consts(erased)
}
pub fn is_in_snapshot(&self) -> bool {
self.in_snapshot.get()
}
@ -1646,26 +1619,31 @@ impl<'tcx> InferCtxt<'tcx> {
// Postpone the evaluation of constants whose substs depend on inference
// variables
let tcx = self.tcx;
if substs.has_non_region_infer() {
let ac = AbstractConst::new(self.tcx, unevaluated);
let substs_erased = tcx.erase_regions(unevaluated.substs);
let ac = tcx.expand_bound_abstract_const(
tcx.bound_abstract_const(unevaluated.def),
substs_erased,
);
match ac {
Ok(None) => {
substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did);
substs = InternalSubsts::identity_for_item(tcx, unevaluated.def.did);
param_env = self.tcx.param_env(unevaluated.def.did);
}
Ok(Some(ct)) => {
if ct.unify_failure_kind(self.tcx) == FailureKind::Concrete {
substs = replace_param_and_infer_substs_with_placeholder(self.tcx, substs);
} 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);
}
}
Err(guar) => return Err(ErrorHandled::Reported(guar)),
}
}
let param_env_erased = self.tcx.erase_regions(param_env);
let substs_erased = self.tcx.erase_regions(substs);
let param_env_erased = tcx.erase_regions(param_env);
let substs_erased = tcx.erase_regions(substs);
debug!(?param_env_erased);
debug!(?substs_erased);
@ -1673,7 +1651,7 @@ impl<'tcx> InferCtxt<'tcx> {
// The return value is the evaluated value which doesn't contain any reference to inference
// variables, thus we don't need to substitute back the original values.
self.tcx.const_eval_resolve_for_typeck(param_env_erased, unevaluated, span)
tcx.const_eval_resolve_for_typeck(param_env_erased, unevaluated, span)
}
/// `ty_or_const_infer_var_changed` is equivalent to one of these two:

View File

@ -644,12 +644,6 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Symbol {
}
}
impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [ty::abstract_const::Node<'tcx>] {
fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self {
ty::codec::RefDecodable::decode(d)
}
}
impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] {
fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self {
ty::codec::RefDecodable::decode(d)

View File

@ -366,7 +366,7 @@ define_tables! {
mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>,
promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>,
// FIXME(compiler-errors): Why isn't this a LazyArray?
thir_abstract_const: Table<DefIndex, LazyValue<&'static [ty::abstract_const::Node<'static>]>>,
thir_abstract_const: Table<DefIndex, LazyValue<ty::Const<'static>>>,
impl_parent: Table<DefIndex, RawDefId>,
impl_polarity: Table<DefIndex, ty::ImplPolarity>,
constness: Table<DefIndex, hir::Constness>,

View File

@ -476,6 +476,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
// These variants shouldn't exist in the MIR.
ty::ConstKind::Placeholder(_)
| ty::ConstKind::Infer(_)
| ty::ConstKind::Expr(_)
| ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", literal),
},
ConstantKind::Unevaluated(uv, _) => {

View File

@ -1185,7 +1185,8 @@ pub enum NullOp {
AlignOf,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
pub enum UnOp {
/// The `!` operator for logical inversion
Not,
@ -1193,7 +1194,8 @@ pub enum UnOp {
Neg,
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
#[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub enum BinOp {
/// The `+` operator (addition)
Add,

View File

@ -16,9 +16,7 @@ TrivialTypeTraversalAndLiftImpls! {
UserTypeAnnotationIndex,
BorrowKind,
CastKind,
BinOp,
NullOp,
UnOp,
hir::Movability,
BasicBlock,
SwitchTargets,

View File

@ -400,7 +400,7 @@ rustc_queries! {
/// Try to build an abstract representation of the given constant.
query thir_abstract_const(
key: DefId
) -> Result<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> {
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
desc {
|tcx| "building an abstract representation for `{}`", tcx.def_path_str(key),
}
@ -409,7 +409,7 @@ rustc_queries! {
/// Try to build an abstract representation of the given constant.
query thir_abstract_const_of_const_arg(
key: (LocalDefId, DefId)
) -> Result<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> {
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
desc {
|tcx|
"building an abstract representation for the const argument `{}`",
@ -417,15 +417,6 @@ rustc_queries! {
}
}
query try_unify_abstract_consts(key:
ty::ParamEnvAnd<'tcx, (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>
)>) -> bool {
desc {
|tcx| "trying to unify the generic constants `{}` and `{}`",
tcx.def_path_str(key.value.0.def.did), tcx.def_path_str(key.value.1.def.did)
}
}
query mir_drops_elaborated_and_const_checked(
key: ty::WithOptConstParam<LocalDefId>
) -> &'tcx Steal<mir::Body<'tcx>> {

View File

@ -1,98 +1,10 @@
//! A subset of a mir body used for const evaluatability checking.
use crate::mir;
use crate::ty::visit::TypeVisitable;
use crate::ty::{self, EarlyBinder, SubstsRef, Ty, TyCtxt};
use crate::ty::{self, Const, EarlyBinder, FallibleTypeFolder, GenericArg, TyCtxt, TypeFoldable};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId;
use std::cmp;
use std::ops::ControlFlow;
rustc_index::newtype_index! {
/// An index into an `AbstractConst`.
pub struct NodeId {
derive [HashStable]
DEBUG_FORMAT = "n{}",
}
}
/// A tree representing an anonymous constant.
///
/// This is only able to represent a subset of `MIR`,
/// and should not leak any information about desugarings.
#[derive(Debug, Clone, Copy)]
pub struct AbstractConst<'tcx> {
// FIXME: Consider adding something like `IndexSlice`
// and use this here.
inner: &'tcx [Node<'tcx>],
substs: SubstsRef<'tcx>,
}
impl<'tcx> AbstractConst<'tcx> {
pub fn new(
tcx: TyCtxt<'tcx>,
uv: ty::UnevaluatedConst<'tcx>,
) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?;
debug!("AbstractConst::new({:?}) = {:?}", uv, inner);
Ok(inner.map(|inner| AbstractConst { inner, substs: tcx.erase_regions(uv.substs) }))
}
pub fn from_const(
tcx: TyCtxt<'tcx>,
ct: ty::Const<'tcx>,
) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
match ct.kind() {
ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv),
ty::ConstKind::Error(reported) => Err(reported),
_ => Ok(None),
}
}
#[inline]
pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> {
AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs }
}
#[inline]
pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> {
let node = self.inner.last().copied().unwrap();
match node {
Node::Leaf(leaf) => Node::Leaf(EarlyBinder(leaf).subst(tcx, self.substs)),
Node::Cast(kind, operand, ty) => {
Node::Cast(kind, operand, EarlyBinder(ty).subst(tcx, self.substs))
}
// Don't perform substitution on the following as they can't directly contain generic params
Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node,
}
}
pub fn unify_failure_kind(self, tcx: TyCtxt<'tcx>) -> FailureKind {
let mut failure_kind = FailureKind::Concrete;
walk_abstract_const::<!, _>(tcx, self, |node| {
match node.root(tcx) {
Node::Leaf(leaf) => {
if leaf.has_non_region_infer() {
failure_kind = FailureKind::MentionsInfer;
} else if leaf.has_non_region_param() {
failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
}
}
Node::Cast(_, _, ty) => {
if ty.has_non_region_infer() {
failure_kind = FailureKind::MentionsInfer;
} else if ty.has_non_region_param() {
failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
}
}
Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {}
}
ControlFlow::CONTINUE
});
failure_kind
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
#[derive(Hash, Debug, Clone, Copy, Ord, PartialOrd, PartialEq, Eq)]
#[derive(TyDecodable, TyEncodable, HashStable, TypeVisitable, TypeFoldable)]
pub enum CastKind {
/// thir::ExprKind::As
As,
@ -100,16 +12,6 @@ pub enum CastKind {
Use,
}
/// A node of an `AbstractConst`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
pub enum Node<'tcx> {
Leaf(ty::Const<'tcx>),
Binop(mir::BinOp, NodeId, NodeId),
UnaryOp(mir::UnOp, NodeId),
FunctionCall(NodeId, &'tcx [NodeId]),
Cast(CastKind, NodeId, Ty<'tcx>),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
pub enum NotConstEvaluatable {
Error(ErrorGuaranteed),
@ -127,68 +29,75 @@ TrivialTypeTraversalAndLiftImpls! {
NotConstEvaluatable,
}
pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>, ErrorGuaranteed>;
impl<'tcx> TyCtxt<'tcx> {
/// Returns a const with substs applied by
pub fn bound_abstract_const(
self,
uv: ty::WithOptConstParam<DefId>,
) -> BoundAbstractConst<'tcx> {
self.thir_abstract_const_opt_const_arg(uv).map(|ac| ac.map(|ac| EarlyBinder(ac)))
}
#[inline]
pub fn thir_abstract_const_opt_const_arg(
self,
def: ty::WithOptConstParam<DefId>,
) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
if let Some((did, param_did)) = def.as_const_arg() {
self.thir_abstract_const_of_const_arg((did, param_did))
} else {
self.thir_abstract_const(def.did)
}
}
}
#[instrument(skip(tcx, f), level = "debug")]
pub fn walk_abstract_const<'tcx, R, F>(
tcx: TyCtxt<'tcx>,
ct: AbstractConst<'tcx>,
mut f: F,
) -> ControlFlow<R>
where
F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
{
#[instrument(skip(tcx, f), level = "debug")]
fn recurse<'tcx, R>(
tcx: TyCtxt<'tcx>,
ct: AbstractConst<'tcx>,
f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
) -> ControlFlow<R> {
f(ct)?;
let root = ct.root(tcx);
debug!(?root);
match root {
Node::Leaf(_) => ControlFlow::CONTINUE,
Node::Binop(_, l, r) => {
recurse(tcx, ct.subtree(l), f)?;
recurse(tcx, ct.subtree(r), f)
}
Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f),
Node::FunctionCall(func, args) => {
recurse(tcx, ct.subtree(func), f)?;
args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f))
}
Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f),
pub fn expand_bound_abstract_const(
self,
ct: BoundAbstractConst<'tcx>,
substs: &[GenericArg<'tcx>],
) -> Result<Option<Const<'tcx>>, ErrorGuaranteed> {
struct Expander<'tcx> {
tcx: TyCtxt<'tcx>,
}
impl<'tcx> FallibleTypeFolder<'tcx> for Expander<'tcx> {
type Error = ErrorGuaranteed;
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, ErrorGuaranteed> {
use ty::ConstKind::*;
let uv = match c.kind() {
Unevaluated(uv) => uv,
Param(..) | Infer(..) | Bound(..) | Placeholder(..) | Value(..) | Error(..) => {
return Ok(c);
}
Expr(e) => {
let new_expr = match e {
ty::Expr::Binop(op, l, r) => {
ty::Expr::Binop(op, l.try_fold_with(self)?, r.try_fold_with(self)?)
}
ty::Expr::UnOp(op, v) => ty::Expr::UnOp(op, v.try_fold_with(self)?),
ty::Expr::Cast(k, c, t) => {
ty::Expr::Cast(k, c.try_fold_with(self)?, t.try_fold_with(self)?)
}
ty::Expr::FunctionCall(func, args) => ty::Expr::FunctionCall(
func.try_fold_with(self)?,
args.try_fold_with(self)?,
),
};
return Ok(self.tcx().mk_const(ty::ConstKind::Expr(new_expr), c.ty()));
}
};
let bac = self.tcx.bound_abstract_const(uv.def);
let ac = self.tcx.expand_bound_abstract_const(bac, uv.substs);
if let Ok(Some(ac)) = ac { ac.try_fold_with(self) } else { Ok(c) }
}
}
let Some(ac) = ct? else {
return Ok(None);
};
let ac = ac.subst(self, substs);
Ok(Some(ac.try_fold_with(&mut Expander { tcx: self })?))
}
recurse(tcx, ct, &mut f)
}
// We were unable to unify the abstract constant with
// a constant found in the caller bounds, there are
// now three possible cases here.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum FailureKind {
/// The abstract const still references an inference
/// variable, in this case we return `TooGeneric`.
MentionsInfer,
/// The abstract const references a generic parameter,
/// this means that we emit an error here.
MentionsParam,
/// The substs are concrete enough that we can simply
/// try and evaluate the given constant.
Concrete,
}

View File

@ -345,26 +345,6 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
}
}
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
for [ty::abstract_const::Node<'tcx>]
{
fn decode(decoder: &mut D) -> &'tcx Self {
decoder.interner().arena.alloc_from_iter(
(0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::<Vec<_>>(),
)
}
}
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
for [ty::abstract_const::NodeId]
{
fn decode(decoder: &mut D) -> &'tcx Self {
decoder.interner().arena.alloc_from_iter(
(0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::<Vec<_>>(),
)
}
}
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
for ty::List<ty::BoundVariableKind>
{
@ -376,6 +356,15 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
}
}
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<ty::Const<'tcx>> {
fn decode(decoder: &mut D) -> &'tcx Self {
let len = decoder.read_usize();
decoder
.interner()
.mk_const_list((0..len).map::<ty::Const<'tcx>, _>(|_| Decodable::decode(decoder)))
}
}
impl_decodable_via_ref! {
&'tcx ty::TypeckResults<'tcx>,
&'tcx ty::List<Ty<'tcx>>,

View File

@ -1,10 +1,12 @@
use std::convert::TryInto;
use super::Const;
use crate::mir;
use crate::mir::interpret::{AllocId, ConstValue, Scalar};
use crate::ty::abstract_const::CastKind;
use crate::ty::subst::{InternalSubsts, SubstsRef};
use crate::ty::ParamEnv;
use crate::ty::{self, TyCtxt, TypeVisitable};
use crate::ty::{self, List, Ty, TyCtxt, TypeVisitable};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId;
@ -70,8 +72,23 @@ pub enum ConstKind<'tcx> {
/// A placeholder for a const which could not be computed; this is
/// propagated to avoid useless error messages.
Error(ErrorGuaranteed),
/// Expr which contains an expression which has partially evaluated items.
Expr(Expr<'tcx>),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
pub enum Expr<'tcx> {
Binop(mir::BinOp, Const<'tcx>, Const<'tcx>),
UnOp(mir::UnOp, Const<'tcx>),
FunctionCall(Const<'tcx>, &'tcx List<Const<'tcx>>),
Cast(CastKind, Const<'tcx>, Ty<'tcx>),
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(Expr<'_>, 24);
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(ConstKind<'_>, 32);

View File

@ -2714,6 +2714,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 { List::from_arena(self.arena, cs) }
}
pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx List<Ty<'tcx>> {
if ts.is_empty() {
List::empty()

View File

@ -356,7 +356,10 @@ impl DeepRejectCtxt {
pub fn consts_may_unify(self, obligation_ct: ty::Const<'_>, impl_ct: ty::Const<'_>) -> bool {
match impl_ct.kind() {
ty::ConstKind::Param(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => {
ty::ConstKind::Expr(_)
| ty::ConstKind::Param(_)
| ty::ConstKind::Unevaluated(_)
| ty::ConstKind::Error(_) => {
return true;
}
ty::ConstKind::Value(_) => {}
@ -374,7 +377,9 @@ impl DeepRejectCtxt {
// As we don't necessarily eagerly evaluate constants,
// they might unify with any value.
ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => true,
ty::ConstKind::Expr(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => {
true
}
ty::ConstKind::Value(obl) => match k {
ty::ConstKind::Value(imp) => obl == imp,
_ => true,

View File

@ -307,6 +307,26 @@ impl FlagComputation {
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
}
ty::ConstKind::Value(_) => {}
ty::ConstKind::Expr(e) => {
use ty::Expr;
match e {
Expr::Binop(_, l, r) => {
self.add_const(l);
self.add_const(r);
}
Expr::UnOp(_, v) => self.add_const(v),
Expr::FunctionCall(f, args) => {
self.add_const(f);
for arg in args {
self.add_const(arg);
}
}
Expr::Cast(_, c, t) => {
self.add_ty(t);
self.add_const(c);
}
}
}
ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR),
}
}

View File

@ -77,7 +77,7 @@ pub use self::closure::{
CAPTURE_STRUCT_LOCAL,
};
pub use self::consts::{
Const, ConstInt, ConstKind, ConstS, InferConst, ScalarInt, UnevaluatedConst, ValTree,
Const, ConstInt, ConstKind, ConstS, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree,
};
pub use self::context::{
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,

View File

@ -4,7 +4,6 @@ use rustc_index::vec::{Idx, IndexVec};
use crate::middle::exported_symbols::ExportedSymbol;
use crate::mir::Body;
use crate::ty::abstract_const::Node;
use crate::ty::{
self, Const, FnSig, GeneratorDiagnosticData, GenericPredicates, Predicate, TraitRef, Ty,
};
@ -124,6 +123,5 @@ parameterized_over_tcx! {
Predicate,
GeneratorDiagnosticData,
Body,
Node,
ExportedSymbol,
}

View File

@ -1253,6 +1253,9 @@ pub trait PrettyPrinter<'tcx>:
self.pretty_print_bound_var(debruijn, bound_var)?
}
ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)),
// FIXME(generic_const_exprs):
// write out some legible representation of an abstract const?
ty::ConstKind::Expr(_) => p!("[Const Expr]"),
ty::ConstKind::Error(_) => p!("[const error]"),
};
Ok(self)

View File

@ -5,7 +5,7 @@
//! subtyping, type equality, etc.
use crate::ty::error::{ExpectedFound, TypeError};
use crate::ty::{self, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable};
use crate::ty::{self, Expr, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable};
use crate::ty::{GenericArg, GenericArgKind, SubstsRef};
use rustc_hir as ast;
use rustc_hir::def_id::DefId;
@ -613,7 +613,10 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
if a_ty != b_ty {
relation.tcx().sess.delay_span_bug(
DUMMY_SP,
&format!("cannot relate constants of different types: {} != {}", a_ty, b_ty),
&format!(
"cannot relate constants ({:?}, {:?}) of different types: {} != {}",
a, b, a_ty, b_ty
),
);
}
@ -647,13 +650,21 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
(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)))
if let (Ok(Some(a)), Ok(Some(b))) = (
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(au.def), au.substs),
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(bu.def), bu.substs),
) && a.ty() == b.ty() {
return relation.consts(a, b);
} else {
false
}
}
// While this is slightly incorrect, it shouldn't matter for `min_const_generics`
// and is the better alternative to waiting until `generic_const_exprs` can
// be stabilized.
(ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if au.def == bu.def => {
assert_eq!(a.ty(), b.ty());
let substs = relation.relate_with_variance(
ty::Variance::Invariant,
ty::VarianceDiagInfo::default(),
@ -665,6 +676,50 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
a.ty(),
));
}
// Before calling relate on exprs, it is necessary to ensure that the nested consts
// have identical types.
(ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => {
let r = relation;
// FIXME(julianknodt): is it possible to relate two consts which are not identical
// exprs? Should we care about that?
let expr = match (ae, be) {
(Expr::Binop(a_op, al, ar), Expr::Binop(b_op, bl, br))
if a_op == b_op && al.ty() == bl.ty() && ar.ty() == br.ty() =>
{
Expr::Binop(a_op, r.consts(al, bl)?, r.consts(ar, br)?)
}
(Expr::UnOp(a_op, av), Expr::UnOp(b_op, bv))
if a_op == b_op && av.ty() == bv.ty() =>
{
Expr::UnOp(a_op, r.consts(av, bv)?)
}
(Expr::Cast(ak, av, at), Expr::Cast(bk, bv, bt))
if ak == bk && av.ty() == bv.ty() =>
{
Expr::Cast(ak, r.consts(av, bv)?, r.tys(at, bt)?)
}
(Expr::FunctionCall(af, aa), Expr::FunctionCall(bf, ba))
if aa.len() == ba.len()
&& af.ty() == bf.ty()
&& aa
.iter()
.zip(ba.iter())
.all(|(a_arg, b_arg)| a_arg.ty() == b_arg.ty()) =>
{
let func = r.consts(af, bf)?;
let mut related_args = Vec::with_capacity(aa.len());
for (a_arg, b_arg) in aa.iter().zip(ba.iter()) {
related_args.push(r.consts(a_arg, b_arg)?);
}
let related_args = tcx.mk_const_list(related_args.iter());
Expr::FunctionCall(func, related_args)
}
_ => return Err(TypeError::ConstMismatch(expected_found(r, a, b))),
};
let kind = ty::ConstKind::Expr(expr);
return Ok(tcx.mk_const(kind, a.ty()));
}
_ => false,
};
if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(expected_found(relation, a, b))) }

View File

@ -593,6 +593,12 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ty::PolyExistentialPredicate<'t
}
}
impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ty::Const<'tcx>> {
fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
ty::util::fold_list(self, folder, |tcx, v| tcx.mk_const_list(v.iter()))
}
}
impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ProjectionKind> {
fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
ty::util::fold_list(self, folder, |tcx, v| tcx.intern_projs(v))

View File

@ -214,6 +214,24 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
| ty::ConstKind::Value(_)
| ty::ConstKind::Error(_) => {}
ty::ConstKind::Expr(expr) => match expr {
ty::Expr::UnOp(_, v) => push_inner(stack, v.into()),
ty::Expr::Binop(_, l, r) => {
push_inner(stack, r.into());
push_inner(stack, l.into())
}
ty::Expr::FunctionCall(func, args) => {
for a in args.iter().rev() {
push_inner(stack, a.into());
}
push_inner(stack, func.into());
}
ty::Expr::Cast(_, c, t) => {
push_inner(stack, t.into());
push_inner(stack, c.into());
}
},
ty::ConstKind::Unevaluated(ct) => {
stack.extend(ct.substs.iter().rev());
}

View File

@ -3,6 +3,7 @@
#![feature(control_flow_enum)]
#![feature(rustc_private)]
#![feature(try_blocks)]
#![feature(let_chains)]
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
@ -25,7 +26,6 @@ use rustc_middle::bug;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::privacy::{EffectiveVisibilities, Level};
use rustc_middle::span_bug;
use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst, Node as ACNode};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind};
@ -286,17 +286,15 @@ where
fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
self.visit_ty(c.ty())?;
let tcx = self.def_id_visitor.tcx();
if let Ok(Some(ct)) = AbstractConst::from_const(tcx, c) {
walk_abstract_const(tcx, ct, |node| match node.root(tcx) {
ACNode::Leaf(leaf) => self.visit_const(leaf),
ACNode::Cast(_, _, ty) => self.visit_ty(ty),
ACNode::Binop(..) | ACNode::UnaryOp(..) | ACNode::FunctionCall(_, _) => {
ControlFlow::CONTINUE
}
})
} else {
ControlFlow::CONTINUE
if let ty::ConstKind::Unevaluated(uv) = c.kind() &&
let Ok(Some(ct)) = tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def),
uv.substs) {
ct.visit_with(self)?;
}
if let ty::ConstKind::Expr(e) = c.kind() {
e.visit_with(self)?;
}
ControlFlow::CONTINUE
}
}

View File

@ -812,12 +812,6 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>>
}
}
impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [ty::abstract_const::Node<'tcx>] {
fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
RefDecodable::decode(d)
}
}
impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] {
fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
RefDecodable::decode(d)

View File

@ -575,6 +575,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
// a path), even for it we still need to encode a placeholder, as
// the path could refer back to e.g. an `impl` using the constant.
ty::ConstKind::Unevaluated(_)
| ty::ConstKind::Expr(_)
| ty::ConstKind::Param(_)
| ty::ConstKind::Infer(_)
| ty::ConstKind::Bound(..)

View File

@ -8,153 +8,16 @@
//! In this case we try to build an abstract representation of this constant using
//! `thir_abstract_const` which can then be checked for structural equality with other
//! generic constants mentioned in the `caller_bounds` of the current environment.
use rustc_errors::ErrorGuaranteed;
use rustc_infer::infer::InferCtxt;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::abstract_const::{
walk_abstract_const, AbstractConst, FailureKind, Node, NotConstEvaluatable,
};
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitor};
use rustc_span::Span;
use std::iter;
use std::ops::ControlFlow;
pub struct ConstUnifyCtxt<'tcx> {
pub tcx: TyCtxt<'tcx>,
pub param_env: ty::ParamEnv<'tcx>,
}
impl<'tcx> ConstUnifyCtxt<'tcx> {
// Substitutes generics repeatedly to allow AbstractConsts to unify where a
// ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g.
// Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])]
#[inline]
#[instrument(skip(self), level = "debug")]
fn try_replace_substs_in_root(
&self,
mut abstr_const: AbstractConst<'tcx>,
) -> Option<AbstractConst<'tcx>> {
while let Node::Leaf(ct) = abstr_const.root(self.tcx) {
match AbstractConst::from_const(self.tcx, ct) {
Ok(Some(act)) => abstr_const = act,
Ok(None) => break,
Err(_) => return None,
}
}
Some(abstr_const)
}
/// Tries to unify two abstract constants using structural equality.
#[instrument(skip(self), level = "debug")]
pub fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool {
let a = if let Some(a) = self.try_replace_substs_in_root(a) {
a
} else {
return true;
};
let b = if let Some(b) = self.try_replace_substs_in_root(b) {
b
} else {
return true;
};
let a_root = a.root(self.tcx);
let b_root = b.root(self.tcx);
debug!(?a_root, ?b_root);
match (a_root, b_root) {
(Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
let a_ct = a_ct.eval(self.tcx, self.param_env);
debug!("a_ct evaluated: {:?}", a_ct);
let b_ct = b_ct.eval(self.tcx, self.param_env);
debug!("b_ct evaluated: {:?}", b_ct);
if a_ct.ty() != b_ct.ty() {
return false;
}
match (a_ct.kind(), b_ct.kind()) {
// We can just unify errors with everything to reduce the amount of
// emitted errors here.
(ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true,
(ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => {
a_param == b_param
}
(ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
// If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]`
// we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This
// means that we only allow inference variables if they are equal.
(ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val,
// We expand generic anonymous constants at the start of this function, so this
// branch should only be taking when dealing with associated constants, at
// which point directly comparing them seems like the desired behavior.
//
// FIXME(generic_const_exprs): This isn't actually the case.
// We also take this branch for concrete anonymous constants and
// expand generic anonymous constants with concrete substs.
(ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => {
a_uv == b_uv
}
// FIXME(generic_const_exprs): We may want to either actually try
// to evaluate `a_ct` and `b_ct` if they are fully concrete or something like
// this, for now we just return false here.
_ => false,
}
}
(Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => {
self.try_unify(a.subtree(al), b.subtree(bl))
&& self.try_unify(a.subtree(ar), b.subtree(br))
}
(Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => {
self.try_unify(a.subtree(av), b.subtree(bv))
}
(Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args))
if a_args.len() == b_args.len() =>
{
self.try_unify(a.subtree(a_f), b.subtree(b_f))
&& iter::zip(a_args, b_args)
.all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn)))
}
(Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty))
if (a_ty == b_ty) && (a_kind == b_kind) =>
{
self.try_unify(a.subtree(a_operand), b.subtree(b_operand))
}
// use this over `_ => false` to make adding variants to `Node` less error prone
(Node::Cast(..), _)
| (Node::FunctionCall(..), _)
| (Node::UnaryOp(..), _)
| (Node::Binop(..), _)
| (Node::Leaf(..), _) => false,
}
}
}
#[instrument(skip(tcx), level = "debug")]
pub fn try_unify_abstract_consts<'tcx>(
tcx: TyCtxt<'tcx>,
(a, b): (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>),
param_env: ty::ParamEnv<'tcx>,
) -> bool {
(|| {
if let Some(a) = AbstractConst::new(tcx, a)? {
if let Some(b) = AbstractConst::new(tcx, b)? {
let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
return Ok(const_unify_ctxt.try_unify(a, b));
}
}
Ok(false)
})()
.unwrap_or_else(|_: ErrorGuaranteed| true)
// FIXME(generic_const_exprs): We should instead have this
// method return the resulting `ty::Const` and return `ConstKind::Error`
// on `ErrorGuaranteed`.
}
/// Check if a given constant can be evaluated.
#[instrument(skip(infcx), level = "debug")]
pub fn is_const_evaluatable<'tcx>(
@ -166,6 +29,8 @@ pub fn is_const_evaluatable<'tcx>(
let tcx = infcx.tcx;
let uv = match ct.kind() {
ty::ConstKind::Unevaluated(uv) => uv,
// should be recursivee fixes.
ty::ConstKind::Expr(..) => todo!(),
ty::ConstKind::Param(_)
| ty::ConstKind::Bound(_, _)
| ty::ConstKind::Placeholder(_)
@ -175,19 +40,17 @@ pub fn is_const_evaluatable<'tcx>(
};
if tcx.features().generic_const_exprs {
if let Some(ct) = AbstractConst::new(tcx, uv)? {
if satisfied_from_param_env(tcx, ct, param_env)? {
let substs = tcx.erase_regions(uv.substs);
if let Some(ct) =
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)?
{
if satisfied_from_param_env(tcx, infcx, ct, param_env)? {
return Ok(());
}
match ct.unify_failure_kind(tcx) {
FailureKind::MentionsInfer => {
return Err(NotConstEvaluatable::MentionsInfer);
}
FailureKind::MentionsParam => {
return Err(NotConstEvaluatable::MentionsParam);
}
// returned below
FailureKind::Concrete => {}
if ct.has_non_region_infer() {
return Err(NotConstEvaluatable::MentionsInfer);
} else if ct.has_non_region_param() {
return Err(NotConstEvaluatable::MentionsParam);
}
}
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
@ -212,13 +75,16 @@ pub fn is_const_evaluatable<'tcx>(
// See #74595 for more details about this.
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
let substs = tcx.erase_regions(uv.substs);
match concrete {
// If we're evaluating a foreign constant, under a nightly compiler without generic
// const exprs, AND it would've passed if that expression had been evaluated with
// generic const exprs, then suggest using generic const exprs.
Err(_) if tcx.sess.is_nightly_build()
&& let Ok(Some(ct)) = AbstractConst::new(tcx, uv)
&& satisfied_from_param_env(tcx, ct, param_env) == Ok(true) => {
&& let Ok(Some(ct)) =
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)
&& let ty::ConstKind::Expr(_expr) = ct.kind()
&& satisfied_from_param_env(tcx, infcx, ct, param_env) == Ok(true) => {
tcx.sess
.struct_span_fatal(
// Slightly better span than just using `span` alone
@ -253,33 +119,60 @@ pub fn is_const_evaluatable<'tcx>(
}
}
#[instrument(skip(tcx), level = "debug")]
#[instrument(skip(infcx, tcx), level = "debug")]
fn satisfied_from_param_env<'tcx>(
tcx: TyCtxt<'tcx>,
ct: AbstractConst<'tcx>,
infcx: &InferCtxt<'tcx>,
ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Result<bool, NotConstEvaluatable> {
for pred in param_env.caller_bounds() {
match pred.kind().skip_binder() {
ty::PredicateKind::ConstEvaluatable(uv) => {
if let Some(b_ct) = AbstractConst::from_const(tcx, uv)? {
let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
let ty::ConstKind::Unevaluated(uv) = uv.kind() else {
continue
};
let substs = tcx.erase_regions(uv.substs);
let Some(b_ct) =
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)? else {
return Ok(false);
};
// Try to unify with each subtree in the AbstractConst to allow for
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
// predicate for `(N + 1) * 2`
let result = walk_abstract_const(tcx, b_ct, |b_ct| {
match const_unify_ctxt.try_unify(ct, b_ct) {
true => ControlFlow::BREAK,
false => ControlFlow::CONTINUE,
// 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 c.ty() == self.ct.ty()
&& let Ok(_nested_obligations) = self
.infcx
.at(&ObligationCause::dummy(), self.param_env)
.eq(c, self.ct)
{
//let obligations = nested_obligations.into_obligations();
ControlFlow::BREAK
} else if let ty::ConstKind::Expr(e) = c.kind() {
e.visit_with(self)
} else {
ControlFlow::CONTINUE
}
});
if let ControlFlow::Break(()) = result {
debug!("is_const_evaluatable: abstract_const ~~> ok");
return Ok(true);
}
}
let mut v = Visitor { ct, infcx, param_env };
let result = b_ct.visit_with(&mut v);
if let ControlFlow::Break(()) = result {
debug!("is_const_evaluatable: abstract_const ~~> ok");
return Ok(true);
}
}
_ => {} // don't care
}

View File

@ -3,6 +3,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::obligation_forest::ProcessResult;
use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome};
use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
use rustc_hir::def::DefKind;
use rustc_infer::traits::ProjectionCacheKey;
use rustc_infer::traits::{SelectionError, TraitEngine, TraitObligation};
use rustc_middle::mir::interpret::ErrorHandled;
@ -452,8 +453,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
}
ty::PredicateKind::ConstEquate(c1, c2) => {
let tcx = self.selcx.tcx();
assert!(
self.selcx.tcx().features().generic_const_exprs,
tcx.features().generic_const_exprs,
"`ConstEquate` without a feature gate: {c1:?} {c2:?}",
);
debug!(?c1, ?c2, "equating consts");
@ -461,12 +463,39 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
// if the constants depend on generic parameters.
//
// Let's just see where this breaks :shrug:
if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
(c1.kind(), c2.kind())
{
if infcx.try_unify_abstract_consts(a, b, obligation.param_env) {
return ProcessResult::Changed(vec![]);
match (c1.kind(), c2.kind()) {
(ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) => {
if tcx.def_kind(a.def.did) == DefKind::AssocConst
|| tcx.def_kind(b.def.did) == DefKind::AssocConst
{
// Two different constants using generic parameters ~> error.
let expected_found = ExpectedFound::new(true, c1, c2);
return ProcessResult::Error(
FulfillmentErrorCode::CodeConstEquateError(
expected_found,
TypeError::ConstMismatch(expected_found),
),
);
}
if let (Ok(Some(a)), Ok(Some(b))) = (
tcx.expand_bound_abstract_const(
tcx.bound_abstract_const(a.def),
a.substs,
),
tcx.expand_bound_abstract_const(
tcx.bound_abstract_const(b.def),
b.substs,
),
) && a.ty() == b.ty() &&
let Ok(new_obligations) = infcx
.at(&obligation.cause, obligation.param_env)
.eq(a, b) {
return ProcessResult::Changed(mk_pending(
new_obligations.into_obligations(),
));
}
}
_ => {}
}
let stalled_on = &mut pending_obligation.stalled_on;

View File

@ -929,10 +929,6 @@ pub fn provide(providers: &mut ty::query::Providers) {
vtable_trait_upcasting_coercion_new_vptr_slot,
subst_and_check_impossible_predicates,
is_impossible_method,
try_unify_abstract_consts: |tcx, param_env_and| {
let (param_env, (a, b)) = param_env_and.into_parts();
const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env)
},
..*providers
};
}

View File

@ -17,11 +17,10 @@ use hir::def::DefKind;
use rustc_errors::{DelayDm, FatalError, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst};
use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
use rustc_middle::ty::{
self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
use rustc_middle::ty::{GenericArg, InternalSubsts};
use rustc_middle::ty::{Predicate, ToPredicate};
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
use rustc_span::symbol::Symbol;
@ -843,15 +842,19 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>(
//
// 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
}
})
//
// We currently recurse into abstract consts here but do not recurse in
// `is_const_evaluatable`. This means that the object safety check is more
// liberal than the const eval check.
//
// This shouldn't really matter though as we can't really use any
// constants which are not considered const evaluatable.
if let ty::ConstKind::Unevaluated(uv) = ct.kind() &&
let Ok(Some(ct)) = self
.tcx
.expand_bound_abstract_const(self.tcx.bound_abstract_const(uv.def), uv.substs)
{
self.visit_const(ct)
} else {
ct.super_visit_with(self)
}

View File

@ -657,8 +657,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
ty::PredicateKind::ConstEquate(c1, c2) => {
let tcx = self.tcx();
assert!(
self.tcx().features().generic_const_exprs,
tcx.features().generic_const_exprs,
"`ConstEquate` without a feature gate: {c1:?} {c2:?}",
);
debug!(?c1, ?c2, "evaluate_predicate_recursively: equating consts");
@ -670,9 +671,28 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
(c1.kind(), c2.kind())
{
if self.infcx.try_unify_abstract_consts(a, b, obligation.param_env) {
return Ok(EvaluatedToOk);
}
if let (Ok(Some(a)), Ok(Some(b))) = (
tcx.expand_bound_abstract_const(
tcx.bound_abstract_const(a.def),
a.substs,
),
tcx.expand_bound_abstract_const(
tcx.bound_abstract_const(b.def),
b.substs,
),
) && a.ty() == b.ty() && let Ok(new_obligations) =
self.infcx.at(&obligation.cause, obligation.param_env).eq(a, b)
{
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(),
);
}
}
let evaluate = |c: ty::Const<'tcx>| {

View File

@ -476,6 +476,10 @@ impl<'tcx> WfPredicates<'tcx> {
ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())),
));
}
// FIXME(julianknodt): need to infer any nested consts here
// so walk and search recursively?
ty::ConstKind::Expr(_) => unimplemented!(),
ty::ConstKind::Error(_)
| ty::ConstKind::Param(_)
| ty::ConstKind::Bound(..)

View File

@ -1,10 +1,11 @@
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_index::vec::IndexVec;
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
use rustc_middle::ty::abstract_const::{CastKind, Node, NodeId};
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
use rustc_middle::thir::visit;
use rustc_middle::thir::visit::Visitor;
use rustc_middle::ty::abstract_const::CastKind;
use rustc_middle::ty::{self, ConstKind, Expr, TyCtxt, TypeVisitable};
use rustc_middle::{mir, thir};
use rustc_span::Span;
use rustc_target::abi::VariantIdx;
@ -76,334 +77,310 @@ pub(crate) fn destructure_const<'tcx>(
ty::DestructuredConst { variant, fields }
}
pub struct AbstractConstBuilder<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
body_id: thir::ExprId,
body: &'a thir::Thir<'tcx>,
/// The current WIP node tree.
nodes: IndexVec<NodeId, Node<'tcx>>,
/// We do not allow all binary operations in abstract consts, so filter disallowed ones.
fn check_binop(op: mir::BinOp) -> bool {
use mir::BinOp::*;
match op {
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le | Ne
| Ge | Gt => true,
Offset => false,
}
}
impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
fn root_span(&self) -> Span {
self.body.exprs[self.body_id].span
/// While we currently allow all unary operations, we still want to explicitly guard against
/// future changes here.
fn check_unop(op: mir::UnOp) -> bool {
use mir::UnOp::*;
match op {
Not | Neg => true,
}
}
fn error(&mut self, sub: GenericConstantTooComplexSub) -> Result<!, ErrorGuaranteed> {
let reported = self.tcx.sess.emit_err(GenericConstantTooComplex {
span: self.root_span(),
maybe_supported: None,
sub,
});
Err(reported)
}
fn maybe_supported_error(
&mut self,
sub: GenericConstantTooComplexSub,
) -> Result<!, ErrorGuaranteed> {
let reported = self.tcx.sess.emit_err(GenericConstantTooComplex {
span: self.root_span(),
maybe_supported: Some(()),
sub,
});
Err(reported)
}
#[instrument(skip(tcx, body, body_id), level = "debug")]
pub fn new(
tcx: TyCtxt<'tcx>,
(body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId),
) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> {
let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() };
struct IsThirPolymorphic<'a, 'tcx> {
is_poly: bool,
thir: &'a thir::Thir<'tcx>,
fn recurse_build<'tcx>(
tcx: TyCtxt<'tcx>,
body: &thir::Thir<'tcx>,
node: thir::ExprId,
root_span: Span,
) -> Result<ty::Const<'tcx>, ErrorGuaranteed> {
use thir::ExprKind;
let node = &body.exprs[node];
Ok(match &node.kind {
// I dont know if handling of these 3 is correct
&ExprKind::Scope { value, .. } => recurse_build(tcx, body, value, root_span)?,
&ExprKind::PlaceTypeAscription { source, .. }
| &ExprKind::ValueTypeAscription { source, .. } => {
recurse_build(tcx, body, source, root_span)?
}
use crate::rustc_middle::thir::visit::Visitor;
use thir::visit;
impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool {
if expr.ty.has_non_region_param() {
return true;
&ExprKind::Literal { lit, neg } => {
let sp = node.span;
match tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) {
Ok(c) => c,
Err(LitToConstError::Reported(guar)) => {
tcx.const_error_with_guaranteed(node.ty, guar)
}
match expr.kind {
thir::ExprKind::NamedConst { substs, .. } => substs.has_non_region_param(),
thir::ExprKind::ConstParam { .. } => true,
thir::ExprKind::Repeat { value, count } => {
self.visit_expr(&self.thir()[value]);
count.has_non_region_param()
}
_ => false,
}
}
fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool {
if pat.ty.has_non_region_param() {
return true;
}
match pat.kind {
thir::PatKind::Constant { value } => value.has_non_region_param(),
thir::PatKind::Range(box thir::PatRange { lo, hi, .. }) => {
lo.has_non_region_param() || hi.has_non_region_param()
}
_ => false,
Err(LitToConstError::TypeError) => {
bug!("encountered type error in lit_to_const")
}
}
}
&ExprKind::NonHirLiteral { lit, user_ty: _ } => {
let val = ty::ValTree::from_scalar_int(lit);
ty::Const::from_value(tcx, val, node.ty)
}
&ExprKind::ZstLiteral { user_ty: _ } => {
let val = ty::ValTree::zst();
ty::Const::from_value(tcx, val, node.ty)
}
&ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
let uneval = ty::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs);
tcx.mk_const(ty::ConstKind::Unevaluated(uneval), node.ty)
}
ExprKind::ConstParam { param, .. } => tcx.mk_const(ty::ConstKind::Param(*param), node.ty),
impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
fn thir(&self) -> &'a thir::Thir<'tcx> {
&self.thir
ExprKind::Call { fun, args, .. } => {
let fun = recurse_build(tcx, body, *fun, root_span)?;
let mut new_args = Vec::<ty::Const<'tcx>>::with_capacity(args.len());
for &id in args.iter() {
new_args.push(recurse_build(tcx, body, id, root_span)?);
}
#[instrument(skip(self), level = "debug")]
fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) {
self.is_poly |= self.expr_is_poly(expr);
if !self.is_poly {
visit::walk_expr(self, expr)
}
}
#[instrument(skip(self), level = "debug")]
fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) {
self.is_poly |= self.pat_is_poly(pat);
if !self.is_poly {
visit::walk_pat(self, pat);
}
let new_args = tcx.mk_const_list(new_args.iter());
tcx.mk_const(ConstKind::Expr(Expr::FunctionCall(fun, new_args)), node.ty)
}
&ExprKind::Binary { op, lhs, rhs } if check_binop(op) => {
let lhs = recurse_build(tcx, body, lhs, root_span)?;
let rhs = recurse_build(tcx, body, rhs, root_span)?;
tcx.mk_const(ConstKind::Expr(Expr::Binop(op, lhs, rhs)), node.ty)
}
&ExprKind::Unary { op, arg } if check_unop(op) => {
let arg = recurse_build(tcx, body, arg, root_span)?;
tcx.mk_const(ConstKind::Expr(Expr::UnOp(op, arg)), node.ty)
}
// This is necessary so that the following compiles:
//
// ```
// fn foo<const N: usize>(a: [(); N + 1]) {
// bar::<{ N + 1 }>();
// }
// ```
ExprKind::Block { block } => {
if let thir::Block { stmts: box [], expr: Some(e), .. } = &body.blocks[*block] {
recurse_build(tcx, body, *e, root_span)?
} else {
maybe_supported_error(
tcx,
GenericConstantTooComplexSub::BlockNotSupported(node.span),
root_span,
)?
}
}
// `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a
// "coercion cast" i.e. using a coercion or is a no-op.
// This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
&ExprKind::Use { source } => {
let arg = recurse_build(tcx, body, source, root_span)?;
tcx.mk_const(ConstKind::Expr(Expr::Cast(CastKind::Use, arg, node.ty)), node.ty)
}
&ExprKind::Cast { source } => {
let arg = recurse_build(tcx, body, source, root_span)?;
tcx.mk_const(ConstKind::Expr(Expr::Cast(CastKind::As, arg, node.ty)), node.ty)
}
ExprKind::Borrow { arg, .. } => {
let arg_node = &body.exprs[*arg];
let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body };
visit::walk_expr(&mut is_poly_vis, &body[body_id]);
debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly);
if !is_poly_vis.is_poly {
return Ok(None);
// Skip reborrows for now until we allow Deref/Borrow/AddressOf
// expressions.
// FIXME(generic_const_exprs): Verify/explain why this is sound
if let ExprKind::Deref { arg } = arg_node.kind {
recurse_build(tcx, body, arg, root_span)?
} else {
maybe_supported_error(
tcx,
GenericConstantTooComplexSub::BorrowNotSupported(node.span),
root_span,
)?
}
}
// FIXME(generic_const_exprs): We may want to support these.
ExprKind::AddressOf { .. } | ExprKind::Deref { .. } => maybe_supported_error(
tcx,
GenericConstantTooComplexSub::AddressAndDerefNotSupported(node.span),
root_span,
)?,
ExprKind::Repeat { .. } | ExprKind::Array { .. } => maybe_supported_error(
tcx,
GenericConstantTooComplexSub::ArrayNotSupported(node.span),
root_span,
)?,
ExprKind::NeverToAny { .. } => maybe_supported_error(
tcx,
GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span),
root_span,
)?,
ExprKind::Tuple { .. } => maybe_supported_error(
tcx,
GenericConstantTooComplexSub::TupleNotSupported(node.span),
root_span,
)?,
ExprKind::Index { .. } => maybe_supported_error(
tcx,
GenericConstantTooComplexSub::IndexNotSupported(node.span),
root_span,
)?,
ExprKind::Field { .. } => maybe_supported_error(
tcx,
GenericConstantTooComplexSub::FieldNotSupported(node.span),
root_span,
)?,
ExprKind::ConstBlock { .. } => maybe_supported_error(
tcx,
GenericConstantTooComplexSub::ConstBlockNotSupported(node.span),
root_span,
)?,
ExprKind::Adt(_) => maybe_supported_error(
tcx,
GenericConstantTooComplexSub::AdtNotSupported(node.span),
root_span,
)?,
// dont know if this is correct
ExprKind::Pointer { .. } => {
error(tcx, GenericConstantTooComplexSub::PointerNotSupported(node.span), root_span)?
}
ExprKind::Yield { .. } => {
error(tcx, GenericConstantTooComplexSub::YieldNotSupported(node.span), root_span)?
}
ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => {
error(tcx, GenericConstantTooComplexSub::LoopNotSupported(node.span), root_span)?
}
ExprKind::Box { .. } => {
error(tcx, GenericConstantTooComplexSub::BoxNotSupported(node.span), root_span)?
}
Ok(Some(builder))
ExprKind::Unary { .. } => unreachable!(),
// we handle valid unary/binary ops above
ExprKind::Binary { .. } => {
error(tcx, GenericConstantTooComplexSub::BinaryNotSupported(node.span), root_span)?
}
ExprKind::LogicalOp { .. } => {
error(tcx, GenericConstantTooComplexSub::LogicalOpNotSupported(node.span), root_span)?
}
ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
error(tcx, GenericConstantTooComplexSub::AssignNotSupported(node.span), root_span)?
}
ExprKind::Closure { .. } | ExprKind::Return { .. } => error(
tcx,
GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span),
root_span,
)?,
// let expressions imply control flow
ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => {
error(tcx, GenericConstantTooComplexSub::ControlFlowNotSupported(node.span), root_span)?
}
ExprKind::InlineAsm { .. } => {
error(tcx, GenericConstantTooComplexSub::InlineAsmNotSupported(node.span), root_span)?
}
// we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
ExprKind::VarRef { .. }
| ExprKind::UpvarRef { .. }
| ExprKind::StaticRef { .. }
| ExprKind::ThreadLocalRef(_) => {
error(tcx, GenericConstantTooComplexSub::OperationNotSupported(node.span), root_span)?
}
})
}
struct IsThirPolymorphic<'a, 'tcx> {
is_poly: bool,
thir: &'a thir::Thir<'tcx>,
}
fn error<'tcx>(
tcx: TyCtxt<'tcx>,
sub: GenericConstantTooComplexSub,
root_span: Span,
) -> Result<!, ErrorGuaranteed> {
let reported = tcx.sess.emit_err(GenericConstantTooComplex {
span: root_span,
maybe_supported: None,
sub,
});
Err(reported)
}
fn maybe_supported_error<'tcx>(
tcx: TyCtxt<'tcx>,
sub: GenericConstantTooComplexSub,
root_span: Span,
) -> Result<!, ErrorGuaranteed> {
let reported = tcx.sess.emit_err(GenericConstantTooComplex {
span: root_span,
maybe_supported: Some(()),
sub,
});
Err(reported)
}
impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool {
if expr.ty.has_non_region_param() {
return true;
}
match expr.kind {
thir::ExprKind::NamedConst { substs, .. } => substs.has_non_region_param(),
thir::ExprKind::ConstParam { .. } => true,
thir::ExprKind::Repeat { value, count } => {
self.visit_expr(&self.thir()[value]);
count.has_non_region_param()
}
_ => false,
}
}
fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool {
if pat.ty.has_non_region_param() {
return true;
}
match pat.kind {
thir::PatKind::Constant { value } => value.has_non_region_param(),
thir::PatKind::Range(box thir::PatRange { lo, hi, .. }) => {
lo.has_non_region_param() || hi.has_non_region_param()
}
_ => false,
}
}
}
impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
fn thir(&self) -> &'a thir::Thir<'tcx> {
&self.thir
}
/// We do not allow all binary operations in abstract consts, so filter disallowed ones.
fn check_binop(op: mir::BinOp) -> bool {
use mir::BinOp::*;
match op {
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le
| Ne | Ge | Gt => true,
Offset => false,
#[instrument(skip(self), level = "debug")]
fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) {
self.is_poly |= self.expr_is_poly(expr);
if !self.is_poly {
visit::walk_expr(self, expr)
}
}
/// While we currently allow all unary operations, we still want to explicitly guard against
/// future changes here.
fn check_unop(op: mir::UnOp) -> bool {
use mir::UnOp::*;
match op {
Not | Neg => true,
#[instrument(skip(self), level = "debug")]
fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) {
self.is_poly |= self.pat_is_poly(pat);
if !self.is_poly {
visit::walk_pat(self, pat);
}
}
/// Builds the abstract const by walking the thir and bailing out when
/// encountering an unsupported operation.
pub fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> {
debug!("AbstractConstBuilder::build: body={:?}", &*self.body);
self.recurse_build(self.body_id)?;
Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter()))
}
fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> {
use thir::ExprKind;
let node = &self.body.exprs[node];
Ok(match &node.kind {
// I dont know if handling of these 3 is correct
&ExprKind::Scope { value, .. } => self.recurse_build(value)?,
&ExprKind::PlaceTypeAscription { source, .. }
| &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?,
&ExprKind::Literal { lit, neg } => {
let sp = node.span;
let constant = match self.tcx.at(sp).lit_to_const(LitToConstInput {
lit: &lit.node,
ty: node.ty,
neg,
}) {
Ok(c) => c,
Err(LitToConstError::Reported(guar)) => {
self.tcx.const_error_with_guaranteed(node.ty, guar)
}
Err(LitToConstError::TypeError) => {
bug!("encountered type error in lit_to_const")
}
};
self.nodes.push(Node::Leaf(constant))
}
&ExprKind::NonHirLiteral { lit, user_ty: _ } => {
let val = ty::ValTree::from_scalar_int(lit);
self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
}
&ExprKind::ZstLiteral { user_ty: _ } => {
let val = ty::ValTree::zst();
self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
}
&ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
let uneval =
ty::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs);
let constant = self.tcx.mk_const(ty::ConstKind::Unevaluated(uneval), node.ty);
self.nodes.push(Node::Leaf(constant))
}
ExprKind::ConstParam { param, .. } => {
let const_param = self.tcx.mk_const(ty::ConstKind::Param(*param), node.ty);
self.nodes.push(Node::Leaf(const_param))
}
ExprKind::Call { fun, args, .. } => {
let fun = self.recurse_build(*fun)?;
let mut new_args = Vec::<NodeId>::with_capacity(args.len());
for &id in args.iter() {
new_args.push(self.recurse_build(id)?);
}
let new_args = self.tcx.arena.alloc_slice(&new_args);
self.nodes.push(Node::FunctionCall(fun, new_args))
}
&ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => {
let lhs = self.recurse_build(lhs)?;
let rhs = self.recurse_build(rhs)?;
self.nodes.push(Node::Binop(op, lhs, rhs))
}
&ExprKind::Unary { op, arg } if Self::check_unop(op) => {
let arg = self.recurse_build(arg)?;
self.nodes.push(Node::UnaryOp(op, arg))
}
// This is necessary so that the following compiles:
//
// ```
// fn foo<const N: usize>(a: [(); N + 1]) {
// bar::<{ N + 1 }>();
// }
// ```
ExprKind::Block { block } => {
if let thir::Block { stmts: box [], expr: Some(e), .. } = &self.body.blocks[*block]
{
self.recurse_build(*e)?
} else {
self.maybe_supported_error(GenericConstantTooComplexSub::BlockNotSupported(
node.span,
))?
}
}
// `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a
// "coercion cast" i.e. using a coercion or is a no-op.
// This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
&ExprKind::Use { source } => {
let arg = self.recurse_build(source)?;
self.nodes.push(Node::Cast(CastKind::Use, arg, node.ty))
}
&ExprKind::Cast { source } => {
let arg = self.recurse_build(source)?;
self.nodes.push(Node::Cast(CastKind::As, arg, node.ty))
}
ExprKind::Borrow { arg, .. } => {
let arg_node = &self.body.exprs[*arg];
// Skip reborrows for now until we allow Deref/Borrow/AddressOf
// expressions.
// FIXME(generic_const_exprs): Verify/explain why this is sound
if let ExprKind::Deref { arg } = arg_node.kind {
self.recurse_build(arg)?
} else {
self.maybe_supported_error(GenericConstantTooComplexSub::BorrowNotSupported(
node.span,
))?
}
}
// FIXME(generic_const_exprs): We may want to support these.
ExprKind::AddressOf { .. } | ExprKind::Deref { .. } => self.maybe_supported_error(
GenericConstantTooComplexSub::AddressAndDerefNotSupported(node.span),
)?,
ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error(
GenericConstantTooComplexSub::ArrayNotSupported(node.span),
)?,
ExprKind::NeverToAny { .. } => self.maybe_supported_error(
GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span),
)?,
ExprKind::Tuple { .. } => self.maybe_supported_error(
GenericConstantTooComplexSub::TupleNotSupported(node.span),
)?,
ExprKind::Index { .. } => self.maybe_supported_error(
GenericConstantTooComplexSub::IndexNotSupported(node.span),
)?,
ExprKind::Field { .. } => self.maybe_supported_error(
GenericConstantTooComplexSub::FieldNotSupported(node.span),
)?,
ExprKind::ConstBlock { .. } => self.maybe_supported_error(
GenericConstantTooComplexSub::ConstBlockNotSupported(node.span),
)?,
ExprKind::Adt(_) => self
.maybe_supported_error(GenericConstantTooComplexSub::AdtNotSupported(node.span))?,
// dont know if this is correct
ExprKind::Pointer { .. } => {
self.error(GenericConstantTooComplexSub::PointerNotSupported(node.span))?
}
ExprKind::Yield { .. } => {
self.error(GenericConstantTooComplexSub::YieldNotSupported(node.span))?
}
ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => {
self.error(GenericConstantTooComplexSub::LoopNotSupported(node.span))?
}
ExprKind::Box { .. } => {
self.error(GenericConstantTooComplexSub::BoxNotSupported(node.span))?
}
ExprKind::Unary { .. } => unreachable!(),
// we handle valid unary/binary ops above
ExprKind::Binary { .. } => {
self.error(GenericConstantTooComplexSub::BinaryNotSupported(node.span))?
}
ExprKind::LogicalOp { .. } => {
self.error(GenericConstantTooComplexSub::LogicalOpNotSupported(node.span))?
}
ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
self.error(GenericConstantTooComplexSub::AssignNotSupported(node.span))?
}
ExprKind::Closure { .. } | ExprKind::Return { .. } => {
self.error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))?
}
// let expressions imply control flow
ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => {
self.error(GenericConstantTooComplexSub::ControlFlowNotSupported(node.span))?
}
ExprKind::InlineAsm { .. } => {
self.error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))?
}
// we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
ExprKind::VarRef { .. }
| ExprKind::UpvarRef { .. }
| ExprKind::StaticRef { .. }
| ExprKind::ThreadLocalRef(_) => {
self.error(GenericConstantTooComplexSub::OperationNotSupported(node.span))?
}
})
}
}
/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
pub fn thir_abstract_const<'tcx>(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
if tcx.features().generic_const_exprs {
match tcx.def_kind(def.did) {
// FIXME(generic_const_exprs): We currently only do this for anonymous constants,
@ -416,10 +393,17 @@ pub fn thir_abstract_const<'tcx>(
}
let body = tcx.thir_body(def)?;
let (body, body_id) = (&*body.0.borrow(), body.1);
AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))?
.map(AbstractConstBuilder::build)
.transpose()
let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body };
visit::walk_expr(&mut is_poly_vis, &body[body_id]);
if !is_poly_vis.is_poly {
return Ok(None);
}
let root_span = body.exprs[body_id].span;
Some(recurse_build(tcx, body, body_id, root_span)).transpose()
} else {
Ok(None)
}

View File

@ -24,10 +24,10 @@ where
fn covariant(
v: &'static Foo<for<'a> fn(&'a ())>
) -> &'static Foo<fn(&'static ())> {
v //~ ERROR mismatched types
v
}
fn main() {
let y = covariant(&Foo([], PhantomData));
let y = covariant(&Foo([], PhantomData)); //~ ERROR mismatched types
println!("{:?}", y.0);
}

View File

@ -13,13 +13,13 @@ LL | impl SadBee for fn(&'static ()) {
= note: `#[warn(coherence_leak_check)]` on by default
error[E0308]: mismatched types
--> $DIR/invariant.rs:27:5
--> $DIR/invariant.rs:31:28
|
LL | v
| ^ one type is more general than the other
LL | let y = covariant(&Foo([], PhantomData));
| ^^ expected `<_ as SadBee>::ASSOC`, found `<for<'a> fn(&'a ()) as SadBee>::ASSOC`
|
= note: expected reference `&Foo<fn(&())>`
found reference `&Foo<for<'a> fn(&'a ())>`
= note: expected constant `<_ as SadBee>::ASSOC`
found constant `<for<'a> fn(&'a ()) as SadBee>::ASSOC`
error: aborting due to previous error; 1 warning emitted

View File

@ -15,9 +15,9 @@ fn foo<T: Foo>(_: [u8; T::N]) -> T {
pub fn bar() {
let _: u8 = foo([0; 1]);
//~^ ERROR mismatched types
let _ = foo([0; 1]);
//~^ ERROR type annotations needed
}
fn main() {}

View File

@ -1,14 +1,12 @@
error[E0282]: type annotations needed
--> $DIR/issue-83249.rs:19:9
error[E0308]: mismatched types
--> $DIR/issue-83249.rs:17:21
|
LL | let _ = foo([0; 1]);
| ^
LL | let _: u8 = foo([0; 1]);
| ^^^^^^ expected `<_ as Foo>::N`, found `<u8 as Foo>::N`
|
help: consider giving this pattern a type
|
LL | let _: _ = foo([0; 1]);
| +++
= note: expected constant `<_ as Foo>::N`
found constant `<u8 as Foo>::N`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.
For more information about this error, try `rustc --explain E0308`.

View File

@ -3,7 +3,6 @@
trait TensorDimension {
const DIM: usize;
//~^ ERROR cycle detected when resolving instance
// FIXME Given the current state of the compiler its expected that we cycle here,
// but the cycle is still wrong.
const ISSCALAR: bool = Self::DIM == 0;
@ -48,6 +47,7 @@ impl<'a, T: Broadcastable, const DIM: usize> TensorDimension for LazyUpdim<'a, T
impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T::DIM }, DIM> {
fn size(&self) -> [usize; DIM] {
//~^ ERROR method not compatible
self.size
}
}
@ -55,12 +55,15 @@ impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T
impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> {
type Element = T::Element;
fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> {
//~^ ERROR method not compatible
assert!(DIM >= T::DIM);
if !self.inbounds(index) {
//~^ ERROR mismatched types
return None;
}
let size = self.size();
let newindex: [usize; T::DIM] = Default::default();
//~^ ERROR the trait bound
self.reference.bget(newindex)
}
}
@ -79,7 +82,10 @@ impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorSi
for BMap<'a, R, T, F, DIM>
{
fn size(&self) -> [usize; DIM] {
//~^ ERROR method not compatible
self.reference.size()
//~^ ERROR unconstrained
//~| ERROR mismatched types
}
}
@ -88,7 +94,10 @@ impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> Broadcas
{
type Element = R;
fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> {
//~^ ERROR method not compatible
self.reference.bget(index).map(&self.closure)
//~^ ERROR unconstrained generic constant
//~| ERROR mismatched types
}
}
@ -111,6 +120,8 @@ fn main() {
let v = vec![1, 2, 3];
let bv = v.lazy_updim([3, 4]);
let bbv = bv.bmap(|x| x * x);
//~^ ERROR mismatched types
println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds."));
//~^ ERROR mismatched types
}

View File

@ -1,21 +1,122 @@
error[E0391]: cycle detected when resolving instance `<LazyUpdim<'_, T, { T::DIM }, DIM> as TensorDimension>::DIM`
--> $DIR/issue-83765.rs:5:5
error[E0308]: method not compatible with trait
--> $DIR/issue-83765.rs:49:5
|
LL | const DIM: usize;
| ^^^^^^^^^^^^^^^^
LL | fn size(&self) -> [usize; DIM] {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
|
note: ...which requires computing candidate for `<LazyUpdim<'_, T, { T::DIM }, DIM> as TensorDimension>`...
--> $DIR/issue-83765.rs:4:1
|
LL | trait TensorDimension {
| ^^^^^^^^^^^^^^^^^^^^^
= note: ...which again requires resolving instance `<LazyUpdim<'_, T, { T::DIM }, DIM> as TensorDimension>::DIM`, completing the cycle
note: cycle used when computing candidate for `<LazyUpdim<'_, T, { T::DIM }, DIM> as TensorDimension>`
--> $DIR/issue-83765.rs:4:1
|
LL | trait TensorDimension {
| ^^^^^^^^^^^^^^^^^^^^^
= note: expected constant `Self::DIM`
found constant `DIM`
error: aborting due to previous error
error[E0308]: method not compatible with trait
--> $DIR/issue-83765.rs:57:5
|
LL | fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
|
= note: expected constant `Self::DIM`
found constant `DIM`
For more information about this error, try `rustc --explain E0391`.
error[E0308]: method not compatible with trait
--> $DIR/issue-83765.rs:84:5
|
LL | fn size(&self) -> [usize; DIM] {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
|
= note: expected constant `Self::DIM`
found constant `DIM`
error[E0308]: method not compatible with trait
--> $DIR/issue-83765.rs:96:5
|
LL | fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
|
= note: expected constant `Self::DIM`
found constant `DIM`
error[E0308]: mismatched types
--> $DIR/issue-83765.rs:60:27
|
LL | if !self.inbounds(index) {
| ^^^^^ expected `Self::DIM`, found `DIM`
|
= note: expected constant `Self::DIM`
found constant `DIM`
error[E0277]: the trait bound `[usize; _]: Default` is not satisfied
--> $DIR/issue-83765.rs:65:41
|
LL | let newindex: [usize; T::DIM] = Default::default();
| ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `[usize; _]`
|
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> where [usize; _]: Default {
| +++++++++++++++++++++++++
error: unconstrained generic constant
--> $DIR/issue-83765.rs:86:24
|
LL | self.reference.size()
| ^^^^
|
= help: try adding a `where` bound using this expression: `where [(); Self::DIM]:`
note: required by a bound in `TensorSize::size`
--> $DIR/issue-83765.rs:15:31
|
LL | fn size(&self) -> [usize; Self::DIM];
| ^^^^^^^^^ required by this bound in `TensorSize::size`
error[E0308]: mismatched types
--> $DIR/issue-83765.rs:86:9
|
LL | self.reference.size()
| ^^^^^^^^^^^^^^^^^^^^^ expected `DIM`, found `Self::DIM`
|
= note: expected constant `DIM`
found constant `Self::DIM`
error: unconstrained generic constant
--> $DIR/issue-83765.rs:98:24
|
LL | self.reference.bget(index).map(&self.closure)
| ^^^^
|
= help: try adding a `where` bound using this expression: `where [(); Self::DIM]:`
note: required by a bound in `Broadcastable::bget`
--> $DIR/issue-83765.rs:23:35
|
LL | fn bget(&self, index: [usize; Self::DIM]) -> Option<Self::Element>;
| ^^^^^^^^^ required by this bound in `Broadcastable::bget`
error[E0308]: mismatched types
--> $DIR/issue-83765.rs:98:29
|
LL | self.reference.bget(index).map(&self.closure)
| ^^^^^ expected `Self::DIM`, found `DIM`
|
= note: expected constant `Self::DIM`
found constant `DIM`
error[E0308]: mismatched types
--> $DIR/issue-83765.rs:122:15
|
LL | let bbv = bv.bmap(|x| x * x);
| ^^^^^^^^^^^^^^^^^^ expected `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`, found `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`
|
= note: expected constant `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`
found constant `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`
error[E0308]: mismatched types
--> $DIR/issue-83765.rs:125:43
|
LL | println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds."));
| ^^^^ expected `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`, found `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`
|
= note: expected constant `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`
found constant `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`
error: aborting due to 12 previous errors
Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.

View File

@ -1,4 +1,8 @@
// revisions: cfail
// check-pass
// known-bug
// This should not compile, as the compiler should not know
// `A - 0` is satisfied `?x - 0` if `?x` is inferred to `A`.
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
@ -6,8 +10,8 @@ pub struct Ref<'a>(&'a i32);
impl<'a> Ref<'a> {
pub fn foo<const A: usize>() -> [(); A - 0] {
//~^ WARN function cannot
Self::foo()
//~^ error: type annotations needed
}
}

View 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