mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Rollup merge of #88709 - BoxyUwU:thir-abstract-const, r=lcnr
generic_const_exprs: use thir for abstract consts instead of mir Changes `AbstractConst` building to use `thir` instead of `mir` so that there's less chance of consts unifying when they shouldn't because lowering to mir dropped information (see `abstract-consts-as-cast-5.rs` test) r? `@lcnr`
This commit is contained in:
commit
f5ac5cadd3
@ -26,6 +26,7 @@ use rustc_middle::middle::cstore::{ForeignModule, LinkagePreference, NativeLib};
|
||||
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
|
||||
use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState};
|
||||
use rustc_middle::mir::{self, Body, Promoted};
|
||||
use rustc_middle::thir;
|
||||
use rustc_middle::ty::codec::TyDecoder;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, Visibility};
|
||||
use rustc_serialize::{opaque, Decodable, Decoder};
|
||||
@ -541,7 +542,7 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [mir::abstract_const::Node<'tcx>] {
|
||||
impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [thir::abstract_const::Node<'tcx>] {
|
||||
fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result<Self, String> {
|
||||
ty::codec::RefDecodable::decode(d)
|
||||
}
|
||||
@ -1196,14 +1197,14 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||
.decode((self, tcx))
|
||||
}
|
||||
|
||||
fn get_mir_abstract_const(
|
||||
fn get_thir_abstract_const(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
id: DefIndex,
|
||||
) -> Result<Option<&'tcx [mir::abstract_const::Node<'tcx>]>, ErrorReported> {
|
||||
) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorReported> {
|
||||
self.root
|
||||
.tables
|
||||
.mir_abstract_consts
|
||||
.thir_abstract_consts
|
||||
.get(self, id)
|
||||
.map_or(Ok(None), |v| Ok(Some(v.decode((self, tcx)))))
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
|
||||
optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) }
|
||||
mir_for_ctfe => { tcx.arena.alloc(cdata.get_mir_for_ctfe(tcx, def_id.index)) }
|
||||
promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) }
|
||||
mir_abstract_const => { cdata.get_mir_abstract_const(tcx, def_id.index) }
|
||||
thir_abstract_const => { cdata.get_thir_abstract_const(tcx, def_id.index) }
|
||||
unused_generic_params => { cdata.get_unused_generic_params(def_id.index) }
|
||||
const_param_default => { tcx.mk_const(cdata.get_const_param_default(tcx, def_id.index)) }
|
||||
mir_const_qualif => { cdata.mir_const_qualif(def_id.index) }
|
||||
|
@ -23,6 +23,7 @@ use rustc_middle::middle::exported_symbols::{
|
||||
metadata_symbol_name, ExportedSymbol, SymbolExportLevel,
|
||||
};
|
||||
use rustc_middle::mir::interpret;
|
||||
use rustc_middle::thir;
|
||||
use rustc_middle::traits::specialization_graph;
|
||||
use rustc_middle::ty::codec::TyEncoder;
|
||||
use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt};
|
||||
@ -344,7 +345,7 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for &'tcx [mir::abstract_const::Node<'tcx>] {
|
||||
impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for &'tcx [thir::abstract_const::Node<'tcx>] {
|
||||
fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult {
|
||||
(**self).encode(s)
|
||||
}
|
||||
@ -1297,9 +1298,10 @@ impl EncodeContext<'a, 'tcx> {
|
||||
if encode_const {
|
||||
record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- self.tcx.mir_for_ctfe(def_id));
|
||||
|
||||
let abstract_const = self.tcx.mir_abstract_const(def_id);
|
||||
// FIXME(generic_const_exprs): this feels wrong to have in `encode_mir`
|
||||
let abstract_const = self.tcx.thir_abstract_const(def_id);
|
||||
if let Ok(Some(abstract_const)) = abstract_const {
|
||||
record!(self.tables.mir_abstract_consts[def_id.to_def_id()] <- abstract_const);
|
||||
record!(self.tables.thir_abstract_consts[def_id.to_def_id()] <- abstract_const);
|
||||
}
|
||||
}
|
||||
record!(self.tables.promoted_mir[def_id.to_def_id()] <- self.tcx.promoted_mir(def_id));
|
||||
|
@ -15,6 +15,7 @@ use rustc_middle::hir::exports::Export;
|
||||
use rustc_middle::middle::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib};
|
||||
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::thir;
|
||||
use rustc_middle::ty::{self, ReprOptions, Ty};
|
||||
use rustc_serialize::opaque::Encoder;
|
||||
use rustc_session::config::SymbolManglingVersion;
|
||||
@ -305,7 +306,7 @@ define_tables! {
|
||||
mir: Table<DefIndex, Lazy!(mir::Body<'tcx>)>,
|
||||
mir_for_ctfe: Table<DefIndex, Lazy!(mir::Body<'tcx>)>,
|
||||
promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::Body<'tcx>>)>,
|
||||
mir_abstract_consts: Table<DefIndex, Lazy!(&'tcx [mir::abstract_const::Node<'tcx>])>,
|
||||
thir_abstract_consts: Table<DefIndex, Lazy!(&'tcx [thir::abstract_const::Node<'tcx>])>,
|
||||
const_defaults: Table<DefIndex, Lazy<rustc_middle::ty::Const<'tcx>>>,
|
||||
unused_generic_params: Table<DefIndex, Lazy<FiniteBitSet<u32>>>,
|
||||
// `def_keys` and `def_path_hashes` represent a lazy version of a
|
||||
|
@ -1,38 +0,0 @@
|
||||
//! A subset of a mir body used for const evaluatability checking.
|
||||
use crate::mir::{self, CastKind};
|
||||
use crate::ty::{self, Ty};
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// An index into an `AbstractConst`.
|
||||
pub struct NodeId {
|
||||
derive [HashStable]
|
||||
DEBUG_FORMAT = "n{}",
|
||||
}
|
||||
}
|
||||
|
||||
/// A node of an `AbstractConst`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
|
||||
pub enum Node<'tcx> {
|
||||
Leaf(&'tcx 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(rustc_errors::ErrorReported),
|
||||
MentionsInfer,
|
||||
MentionsParam,
|
||||
}
|
||||
|
||||
impl From<rustc_errors::ErrorReported> for NotConstEvaluatable {
|
||||
fn from(e: rustc_errors::ErrorReported) -> NotConstEvaluatable {
|
||||
NotConstEvaluatable::Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
TrivialTypeFoldableAndLiftImpls! {
|
||||
NotConstEvaluatable,
|
||||
}
|
@ -40,7 +40,6 @@ use self::graph_cyclic_cache::GraphIsCyclicCache;
|
||||
use self::predecessors::{PredecessorCache, Predecessors};
|
||||
pub use self::query::*;
|
||||
|
||||
pub mod abstract_const;
|
||||
pub mod coverage;
|
||||
mod generic_graph;
|
||||
pub mod generic_graphviz;
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Values computed by queries that use MIR.
|
||||
|
||||
use crate::mir::{abstract_const, Body, Promoted};
|
||||
use crate::mir::{Body, Promoted};
|
||||
use crate::ty::{self, Ty, TyCtxt};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::vec_map::VecMap;
|
||||
@ -431,16 +431,4 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
self.mir_for_ctfe(def.did)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mir_abstract_const_opt_const_arg(
|
||||
self,
|
||||
def: ty::WithOptConstParam<DefId>,
|
||||
) -> Result<Option<&'tcx [abstract_const::Node<'tcx>]>, ErrorReported> {
|
||||
if let Some((did, param_did)) = def.as_const_arg() {
|
||||
self.mir_abstract_const_of_const_arg((did, param_did))
|
||||
} else {
|
||||
self.mir_abstract_const(def.did)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -303,17 +303,17 @@ rustc_queries! {
|
||||
}
|
||||
|
||||
/// Try to build an abstract representation of the given constant.
|
||||
query mir_abstract_const(
|
||||
query thir_abstract_const(
|
||||
key: DefId
|
||||
) -> Result<Option<&'tcx [mir::abstract_const::Node<'tcx>]>, ErrorReported> {
|
||||
) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorReported> {
|
||||
desc {
|
||||
|tcx| "building an abstract representation for {}", tcx.def_path_str(key),
|
||||
}
|
||||
}
|
||||
/// Try to build an abstract representation of the given constant.
|
||||
query mir_abstract_const_of_const_arg(
|
||||
query thir_abstract_const_of_const_arg(
|
||||
key: (LocalDefId, DefId)
|
||||
) -> Result<Option<&'tcx [mir::abstract_const::Node<'tcx>]>, ErrorReported> {
|
||||
) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorReported> {
|
||||
desc {
|
||||
|tcx|
|
||||
"building an abstract representation for the const argument {}",
|
||||
|
@ -33,6 +33,9 @@ use rustc_target::asm::InlineAsmRegOrRegClass;
|
||||
use std::fmt;
|
||||
use std::ops::Index;
|
||||
|
||||
pub mod abstract_const;
|
||||
pub mod visit;
|
||||
|
||||
newtype_index! {
|
||||
/// An index to an [`Arm`] stored in [`Thir::arms`]
|
||||
#[derive(HashStable)]
|
||||
|
61
compiler/rustc_middle/src/thir/abstract_const.rs
Normal file
61
compiler/rustc_middle/src/thir/abstract_const.rs
Normal file
@ -0,0 +1,61 @@
|
||||
//! A subset of a mir body used for const evaluatability checking.
|
||||
use crate::mir;
|
||||
use crate::ty::{self, Ty, TyCtxt};
|
||||
use rustc_errors::ErrorReported;
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// An index into an `AbstractConst`.
|
||||
pub struct NodeId {
|
||||
derive [HashStable]
|
||||
DEBUG_FORMAT = "n{}",
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
|
||||
pub enum CastKind {
|
||||
/// thir::ExprKind::As
|
||||
As,
|
||||
/// thir::ExprKind::Use
|
||||
Use,
|
||||
}
|
||||
|
||||
/// A node of an `AbstractConst`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
|
||||
pub enum Node<'tcx> {
|
||||
Leaf(&'tcx 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(ErrorReported),
|
||||
MentionsInfer,
|
||||
MentionsParam,
|
||||
}
|
||||
|
||||
impl From<ErrorReported> for NotConstEvaluatable {
|
||||
fn from(e: ErrorReported) -> NotConstEvaluatable {
|
||||
NotConstEvaluatable::Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
TrivialTypeFoldableAndLiftImpls! {
|
||||
NotConstEvaluatable,
|
||||
}
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
#[inline]
|
||||
pub fn thir_abstract_const_opt_const_arg(
|
||||
self,
|
||||
def: ty::WithOptConstParam<rustc_hir::def_id::DefId>,
|
||||
) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorReported> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
use rustc_middle::thir::{self, *};
|
||||
use super::{
|
||||
Arm, Block, Expr, ExprKind, Guard, InlineAsmOperand, Pat, PatKind, Stmt, StmtKind, Thir,
|
||||
};
|
||||
use rustc_middle::ty::Const;
|
||||
|
||||
pub trait Visitor<'a, 'tcx: 'a>: Sized {
|
||||
@ -101,7 +103,7 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
|
||||
visitor.visit_expr(&visitor.thir()[field]);
|
||||
}
|
||||
}
|
||||
Adt(box thir::Adt {
|
||||
Adt(box crate::thir::Adt {
|
||||
ref fields,
|
||||
ref base,
|
||||
adt_def: _,
|
@ -9,7 +9,7 @@ pub mod specialization_graph;
|
||||
mod structural_impls;
|
||||
|
||||
use crate::infer::canonical::Canonical;
|
||||
use crate::mir::abstract_const::NotConstEvaluatable;
|
||||
use crate::thir::abstract_const::NotConstEvaluatable;
|
||||
use crate::ty::subst::SubstsRef;
|
||||
use crate::ty::{self, AdtKind, Ty, TyCtxt};
|
||||
|
||||
|
@ -12,6 +12,7 @@ use crate::mir::{
|
||||
self,
|
||||
interpret::{AllocId, Allocation},
|
||||
};
|
||||
use crate::thir;
|
||||
use crate::ty::subst::SubstsRef;
|
||||
use crate::ty::{self, List, Ty, TyCtxt};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
@ -362,7 +363,7 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [(ty::Predicate<'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::Node<'tcx>] {
|
||||
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [thir::abstract_const::Node<'tcx>] {
|
||||
fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
|
||||
Ok(decoder.tcx().arena.alloc_from_iter(
|
||||
(0..decoder.read_usize()?)
|
||||
@ -372,7 +373,7 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::N
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::NodeId] {
|
||||
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [thir::abstract_const::NodeId] {
|
||||
fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
|
||||
Ok(decoder.tcx().arena.alloc_from_iter(
|
||||
(0..decoder.read_usize()?)
|
||||
|
@ -47,10 +47,12 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_
|
||||
// Ensure unsafeck is ran before we steal the THIR.
|
||||
match def {
|
||||
ty::WithOptConstParam { did, const_param_did: Some(const_param_did) } => {
|
||||
tcx.ensure().thir_check_unsafety_for_const_arg((did, const_param_did))
|
||||
tcx.ensure().thir_check_unsafety_for_const_arg((did, const_param_did));
|
||||
tcx.ensure().thir_abstract_const_of_const_arg((did, const_param_did));
|
||||
}
|
||||
ty::WithOptConstParam { did, const_param_did: None } => {
|
||||
tcx.ensure().thir_check_unsafety(did)
|
||||
tcx.ensure().thir_check_unsafety(did);
|
||||
tcx.ensure().thir_abstract_const(did);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::build::ExprCategory;
|
||||
use crate::thir::visit::{self, Visitor};
|
||||
use rustc_middle::thir::visit::{self, Visitor};
|
||||
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
|
@ -11,4 +11,3 @@ crate mod cx;
|
||||
crate mod pattern;
|
||||
|
||||
mod util;
|
||||
pub mod visit;
|
||||
|
@ -306,7 +306,6 @@ fn mir_promoted(
|
||||
// this point, before we steal the mir-const result.
|
||||
// Also this means promotion can rely on all const checks having been done.
|
||||
let _ = tcx.mir_const_qualif_opt_const_arg(def);
|
||||
let _ = tcx.mir_abstract_const_opt_const_arg(def.to_global());
|
||||
let mut body = tcx.mir_const(def).steal();
|
||||
|
||||
let mut required_consts = Vec::new();
|
||||
|
@ -19,8 +19,8 @@ use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::middle::privacy::{AccessLevel, AccessLevels};
|
||||
use rustc_middle::mir::abstract_const::Node as ACNode;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::thir::abstract_const::Node as ACNode;
|
||||
use rustc_middle::ty::fold::TypeVisitor;
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::subst::{InternalSubsts, Subst};
|
||||
|
@ -9,6 +9,7 @@ use rustc_index::vec::{Idx, IndexVec};
|
||||
use rustc_middle::dep_graph::{DepNode, DepNodeIndex, SerializedDepNodeIndex};
|
||||
use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState};
|
||||
use rustc_middle::mir::{self, interpret};
|
||||
use rustc_middle::thir;
|
||||
use rustc_middle::ty::codec::{RefDecodable, TyDecoder, TyEncoder};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_query_system::dep_graph::DepContext;
|
||||
@ -921,7 +922,7 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [mir::abstract_const::Node<'tcx>] {
|
||||
impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [thir::abstract_const::Node<'tcx>] {
|
||||
fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result<Self, String> {
|
||||
RefDecodable::decode(d)
|
||||
}
|
||||
|
@ -6,16 +6,16 @@
|
||||
//! this is not as easy.
|
||||
//!
|
||||
//! In this case we try to build an abstract representation of this constant using
|
||||
//! `mir_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.
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_middle::mir::abstract_const::{Node, NodeId, NotConstEvaluatable};
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::mir::{self, Rvalue, StatementKind, TerminatorKind};
|
||||
use rustc_middle::thir;
|
||||
use rustc_middle::thir::abstract_const::{self, Node, NodeId, NotConstEvaluatable};
|
||||
use rustc_middle::ty::subst::{Subst, SubstsRef};
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
|
||||
use rustc_session::lint;
|
||||
@ -196,7 +196,7 @@ impl<'tcx> AbstractConst<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
uv: ty::Unevaluated<'tcx, ()>,
|
||||
) -> Result<Option<AbstractConst<'tcx>>, ErrorReported> {
|
||||
let inner = tcx.mir_abstract_const_opt_const_arg(uv.def)?;
|
||||
let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?;
|
||||
debug!("AbstractConst::new({:?}) = {:?}", uv, inner);
|
||||
Ok(inner.map(|inner| AbstractConst { inner, substs: uv.substs(tcx) }))
|
||||
}
|
||||
@ -223,35 +223,24 @@ impl<'tcx> AbstractConst<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct WorkNode<'tcx> {
|
||||
node: Node<'tcx>,
|
||||
span: Span,
|
||||
used: bool,
|
||||
}
|
||||
|
||||
struct AbstractConstBuilder<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &'a mir::Body<'tcx>,
|
||||
body_id: thir::ExprId,
|
||||
body: &'a thir::Thir<'tcx>,
|
||||
/// The current WIP node tree.
|
||||
///
|
||||
/// We require all nodes to be used in the final abstract const,
|
||||
/// so we store this here. Note that we also consider nodes as used
|
||||
/// if they are mentioned in an assert, so some used nodes are never
|
||||
/// actually reachable by walking the [`AbstractConst`].
|
||||
nodes: IndexVec<NodeId, WorkNode<'tcx>>,
|
||||
locals: IndexVec<mir::Local, NodeId>,
|
||||
/// We only allow field accesses if they access
|
||||
/// the result of a checked operation.
|
||||
checked_op_locals: BitSet<mir::Local>,
|
||||
nodes: IndexVec<NodeId, Node<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
||||
fn root_span(&self) -> Span {
|
||||
self.body.exprs[self.body_id].span
|
||||
}
|
||||
|
||||
fn error(&mut self, span: Option<Span>, msg: &str) -> Result<!, ErrorReported> {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(self.body.span, "overly complex generic constant")
|
||||
.span_label(span.unwrap_or(self.body.span), msg)
|
||||
.struct_span_err(self.root_span(), "overly complex generic constant")
|
||||
.span_label(span.unwrap_or(self.root_span()), msg)
|
||||
.help("consider moving this anonymous constant into a `const` function")
|
||||
.emit();
|
||||
|
||||
@ -260,100 +249,51 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
||||
|
||||
fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &'a mir::Body<'tcx>,
|
||||
(body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId),
|
||||
) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorReported> {
|
||||
let mut builder = AbstractConstBuilder {
|
||||
tcx,
|
||||
body,
|
||||
nodes: IndexVec::new(),
|
||||
locals: IndexVec::from_elem(NodeId::MAX, &body.local_decls),
|
||||
checked_op_locals: BitSet::new_empty(body.local_decls.len()),
|
||||
};
|
||||
let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() };
|
||||
|
||||
// We don't have to look at concrete constants, as we
|
||||
// can just evaluate them.
|
||||
if !body.is_polymorphic {
|
||||
struct IsThirPolymorphic<'a, 'tcx> {
|
||||
is_poly: bool,
|
||||
thir: &'a thir::Thir<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
use thir::visit;
|
||||
impl<'a, 'tcx: 'a> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
|
||||
fn thir(&self) -> &'a thir::Thir<'tcx> {
|
||||
&self.thir
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) {
|
||||
self.is_poly |= expr.ty.definitely_has_param_types_or_consts(self.tcx);
|
||||
if self.is_poly == false {
|
||||
visit::walk_expr(self, expr)
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) {
|
||||
self.is_poly |= pat.ty.definitely_has_param_types_or_consts(self.tcx);
|
||||
if self.is_poly == false {
|
||||
visit::walk_pat(self, pat);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) {
|
||||
self.is_poly |= ct.definitely_has_param_types_or_consts(self.tcx);
|
||||
}
|
||||
}
|
||||
|
||||
let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body, tcx };
|
||||
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 == false {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// We only allow consts without control flow, so
|
||||
// we check for cycles here which simplifies the
|
||||
// rest of this implementation.
|
||||
if body.is_cfg_cyclic() {
|
||||
builder.error(None, "cyclic anonymous constants are forbidden")?;
|
||||
}
|
||||
|
||||
Ok(Some(builder))
|
||||
}
|
||||
|
||||
fn add_node(&mut self, node: Node<'tcx>, span: Span) -> NodeId {
|
||||
// Mark used nodes.
|
||||
match node {
|
||||
Node::Leaf(_) => (),
|
||||
Node::Binop(_, lhs, rhs) => {
|
||||
self.nodes[lhs].used = true;
|
||||
self.nodes[rhs].used = true;
|
||||
}
|
||||
Node::UnaryOp(_, input) => {
|
||||
self.nodes[input].used = true;
|
||||
}
|
||||
Node::FunctionCall(func, nodes) => {
|
||||
self.nodes[func].used = true;
|
||||
nodes.iter().for_each(|&n| self.nodes[n].used = true);
|
||||
}
|
||||
Node::Cast(_, operand, _) => {
|
||||
self.nodes[operand].used = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Nodes start as unused.
|
||||
self.nodes.push(WorkNode { node, span, used: false })
|
||||
}
|
||||
|
||||
fn place_to_local(
|
||||
&mut self,
|
||||
span: Span,
|
||||
p: &mir::Place<'tcx>,
|
||||
) -> Result<mir::Local, ErrorReported> {
|
||||
const ZERO_FIELD: mir::Field = mir::Field::from_usize(0);
|
||||
// Do not allow any projections.
|
||||
//
|
||||
// One exception are field accesses on the result of checked operations,
|
||||
// which are required to support things like `1 + 2`.
|
||||
if let Some(p) = p.as_local() {
|
||||
debug_assert!(!self.checked_op_locals.contains(p));
|
||||
Ok(p)
|
||||
} else if let &[mir::ProjectionElem::Field(ZERO_FIELD, _)] = p.projection.as_ref() {
|
||||
// Only allow field accesses if the given local
|
||||
// contains the result of a checked operation.
|
||||
if self.checked_op_locals.contains(p.local) {
|
||||
Ok(p.local)
|
||||
} else {
|
||||
self.error(Some(span), "unsupported projection")?;
|
||||
}
|
||||
} else {
|
||||
self.error(Some(span), "unsupported projection")?;
|
||||
}
|
||||
}
|
||||
|
||||
fn operand_to_node(
|
||||
&mut self,
|
||||
span: Span,
|
||||
op: &mir::Operand<'tcx>,
|
||||
) -> Result<NodeId, ErrorReported> {
|
||||
debug!("operand_to_node: op={:?}", op);
|
||||
match op {
|
||||
mir::Operand::Copy(p) | mir::Operand::Move(p) => {
|
||||
let local = self.place_to_local(span, p)?;
|
||||
Ok(self.locals[local])
|
||||
}
|
||||
mir::Operand::Constant(ct) => match ct.literal {
|
||||
mir::ConstantKind::Ty(ct) => Ok(self.add_node(Node::Leaf(ct), span)),
|
||||
mir::ConstantKind::Val(..) => self.error(Some(span), "unsupported constant")?,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// We do not allow all binary operations in abstract consts, so filter disallowed ones.
|
||||
fn check_binop(op: mir::BinOp) -> bool {
|
||||
use mir::BinOp::*;
|
||||
@ -373,170 +313,126 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_statement(&mut self, stmt: &mir::Statement<'tcx>) -> Result<(), ErrorReported> {
|
||||
debug!("AbstractConstBuilder: stmt={:?}", stmt);
|
||||
let span = stmt.source_info.span;
|
||||
match stmt.kind {
|
||||
StatementKind::Assign(box (ref place, ref rvalue)) => {
|
||||
let local = self.place_to_local(span, place)?;
|
||||
match *rvalue {
|
||||
Rvalue::Use(ref operand) => {
|
||||
self.locals[local] = self.operand_to_node(span, operand)?;
|
||||
Ok(())
|
||||
}
|
||||
Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) if Self::check_binop(op) => {
|
||||
let lhs = self.operand_to_node(span, lhs)?;
|
||||
let rhs = self.operand_to_node(span, rhs)?;
|
||||
self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs), span);
|
||||
if op.is_checkable() {
|
||||
bug!("unexpected unchecked checkable binary operation");
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs))
|
||||
if Self::check_binop(op) =>
|
||||
{
|
||||
let lhs = self.operand_to_node(span, lhs)?;
|
||||
let rhs = self.operand_to_node(span, rhs)?;
|
||||
self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs), span);
|
||||
self.checked_op_locals.insert(local);
|
||||
Ok(())
|
||||
}
|
||||
Rvalue::UnaryOp(op, ref operand) if Self::check_unop(op) => {
|
||||
let operand = self.operand_to_node(span, operand)?;
|
||||
self.locals[local] = self.add_node(Node::UnaryOp(op, operand), span);
|
||||
Ok(())
|
||||
}
|
||||
Rvalue::Cast(cast_kind, ref operand, ty) => {
|
||||
let operand = self.operand_to_node(span, operand)?;
|
||||
self.locals[local] =
|
||||
self.add_node(Node::Cast(cast_kind, operand, ty), span);
|
||||
Ok(())
|
||||
}
|
||||
_ => self.error(Some(span), "unsupported rvalue")?,
|
||||
}
|
||||
}
|
||||
// These are not actually relevant for us here, so we can ignore them.
|
||||
StatementKind::AscribeUserType(..)
|
||||
| StatementKind::StorageLive(_)
|
||||
| StatementKind::StorageDead(_) => Ok(()),
|
||||
_ => self.error(Some(stmt.source_info.span), "unsupported statement")?,
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible return values:
|
||||
///
|
||||
/// - `None`: unsupported terminator, stop building
|
||||
/// - `Some(None)`: supported terminator, finish building
|
||||
/// - `Some(Some(block))`: support terminator, build `block` next
|
||||
fn build_terminator(
|
||||
&mut self,
|
||||
terminator: &mir::Terminator<'tcx>,
|
||||
) -> Result<Option<mir::BasicBlock>, ErrorReported> {
|
||||
debug!("AbstractConstBuilder: terminator={:?}", terminator);
|
||||
match terminator.kind {
|
||||
TerminatorKind::Goto { target } => Ok(Some(target)),
|
||||
TerminatorKind::Return => Ok(None),
|
||||
TerminatorKind::Call {
|
||||
ref func,
|
||||
ref args,
|
||||
destination: Some((ref place, target)),
|
||||
// We do not care about `cleanup` here. Any branch which
|
||||
// uses `cleanup` will fail const-eval and they therefore
|
||||
// do not matter when checking for const evaluatability.
|
||||
//
|
||||
// Do note that even if `panic::catch_unwind` is made const,
|
||||
// we still do not have to care about this, as we do not look
|
||||
// into functions.
|
||||
cleanup: _,
|
||||
// Do not allow overloaded operators for now,
|
||||
// we probably do want to allow this in the future.
|
||||
//
|
||||
// This is currently fairly irrelevant as it requires `const Trait`s.
|
||||
from_hir_call: true,
|
||||
fn_span,
|
||||
} => {
|
||||
let local = self.place_to_local(fn_span, place)?;
|
||||
let func = self.operand_to_node(fn_span, func)?;
|
||||
let args = self.tcx.arena.alloc_from_iter(
|
||||
args.iter()
|
||||
.map(|arg| self.operand_to_node(terminator.source_info.span, arg))
|
||||
.collect::<Result<Vec<NodeId>, _>>()?,
|
||||
);
|
||||
self.locals[local] = self.add_node(Node::FunctionCall(func, args), fn_span);
|
||||
Ok(Some(target))
|
||||
}
|
||||
TerminatorKind::Assert { ref cond, expected: false, target, .. } => {
|
||||
let p = match cond {
|
||||
mir::Operand::Copy(p) | mir::Operand::Move(p) => p,
|
||||
mir::Operand::Constant(_) => bug!("unexpected assert"),
|
||||
};
|
||||
|
||||
const ONE_FIELD: mir::Field = mir::Field::from_usize(1);
|
||||
debug!("proj: {:?}", p.projection);
|
||||
if let Some(p) = p.as_local() {
|
||||
debug_assert!(!self.checked_op_locals.contains(p));
|
||||
// Mark locals directly used in asserts as used.
|
||||
//
|
||||
// This is needed because division does not use `CheckedBinop` but instead
|
||||
// adds an explicit assert for `divisor != 0`.
|
||||
self.nodes[self.locals[p]].used = true;
|
||||
return Ok(Some(target));
|
||||
} else if let &[mir::ProjectionElem::Field(ONE_FIELD, _)] = p.projection.as_ref() {
|
||||
// Only allow asserts checking the result of a checked operation.
|
||||
if self.checked_op_locals.contains(p.local) {
|
||||
return Ok(Some(target));
|
||||
}
|
||||
}
|
||||
|
||||
self.error(Some(terminator.source_info.span), "unsupported assertion")?;
|
||||
}
|
||||
_ => self.error(Some(terminator.source_info.span), "unsupported terminator")?,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the abstract const by walking the mir from start to finish
|
||||
/// and bailing out when encountering an unsupported operation.
|
||||
/// Builds the abstract const by walking the thir and bailing out when
|
||||
/// encountering an unspported operation.
|
||||
fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorReported> {
|
||||
let mut block = &self.body.basic_blocks()[mir::START_BLOCK];
|
||||
// We checked for a cyclic cfg above, so this should terminate.
|
||||
loop {
|
||||
debug!("AbstractConstBuilder: block={:?}", block);
|
||||
for stmt in block.statements.iter() {
|
||||
self.build_statement(stmt)?;
|
||||
}
|
||||
debug!("Abstractconstbuilder::build: body={:?}", &*self.body);
|
||||
self.recurse_build(self.body_id)?;
|
||||
|
||||
if let Some(next) = self.build_terminator(block.terminator())? {
|
||||
block = &self.body.basic_blocks()[next];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(self.locals[mir::RETURN_PLACE], self.nodes.last().unwrap());
|
||||
for n in self.nodes.iter() {
|
||||
if let Node::Leaf(ty::Const { val: ty::ConstKind::Unevaluated(ct), ty: _ }) = n.node {
|
||||
if let Node::Leaf(ty::Const { val: ty::ConstKind::Unevaluated(ct), ty: _ }) = n {
|
||||
// `AbstractConst`s should not contain any promoteds as they require references which
|
||||
// are not allowed.
|
||||
assert_eq!(ct.promoted, None);
|
||||
}
|
||||
}
|
||||
|
||||
self.nodes[self.locals[mir::RETURN_PLACE]].used = true;
|
||||
if let Some(&unused) = self.nodes.iter().find(|n| !n.used) {
|
||||
self.error(Some(unused.span), "dead code")?;
|
||||
}
|
||||
Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter()))
|
||||
}
|
||||
|
||||
Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter().map(|n| n.node)))
|
||||
fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorReported> {
|
||||
use thir::ExprKind;
|
||||
let node = &self.body.exprs[node];
|
||||
debug!("recurse_build: node={:?}", 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)?,
|
||||
|
||||
// subtle: associated consts are literals this arm handles
|
||||
// `<T as Trait>::ASSOC` as well as `12`
|
||||
&ExprKind::Literal { literal, .. } => self.nodes.push(Node::Leaf(literal)),
|
||||
|
||||
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 { body: thir::Block { stmts: box [], expr: Some(e), .. }} => self.recurse_build(*e)?,
|
||||
// `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(abstract_const::CastKind::Use, arg, node.ty))
|
||||
},
|
||||
&ExprKind::Cast { source } => {
|
||||
let arg = self.recurse_build(source)?;
|
||||
self.nodes.push(Node::Cast(abstract_const::CastKind::As, arg, node.ty))
|
||||
},
|
||||
|
||||
// FIXME(generic_const_exprs): We may want to support these.
|
||||
ExprKind::AddressOf { .. }
|
||||
| ExprKind::Borrow { .. }
|
||||
| ExprKind::Deref { .. }
|
||||
| ExprKind::Repeat { .. }
|
||||
| ExprKind::Array { .. }
|
||||
| ExprKind::Block { .. }
|
||||
| ExprKind::NeverToAny { .. }
|
||||
| ExprKind::Tuple { .. }
|
||||
| ExprKind::Index { .. }
|
||||
| ExprKind::Field { .. }
|
||||
| ExprKind::ConstBlock { .. }
|
||||
| ExprKind::Adt(_) => self.error(
|
||||
Some(node.span),
|
||||
"unsupported operation in generic constant, this may be supported in the future",
|
||||
)?,
|
||||
|
||||
ExprKind::Match { .. }
|
||||
// we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
|
||||
| ExprKind::VarRef { .. }
|
||||
| ExprKind::UpvarRef { .. }
|
||||
| ExprKind::Closure { .. }
|
||||
| ExprKind::Let { .. } // let expressions imply control flow
|
||||
| ExprKind::Loop { .. }
|
||||
| ExprKind::Assign { .. }
|
||||
| ExprKind::StaticRef { .. }
|
||||
| ExprKind::LogicalOp { .. }
|
||||
// we handle valid unary/binary ops above
|
||||
| ExprKind::Unary { .. }
|
||||
| ExprKind::Binary { .. }
|
||||
| ExprKind::Break { .. }
|
||||
| ExprKind::Continue { .. }
|
||||
| ExprKind::If { .. }
|
||||
| ExprKind::Pointer { .. } // dont know if this is correct
|
||||
| ExprKind::ThreadLocalRef(_)
|
||||
| ExprKind::LlvmInlineAsm { .. }
|
||||
| ExprKind::Return { .. }
|
||||
| ExprKind::Box { .. } // allocations not allowed in constants
|
||||
| ExprKind::AssignOp { .. }
|
||||
| ExprKind::InlineAsm { .. }
|
||||
| ExprKind::Yield { .. } => self.error(Some(node.span), "unsupported operation in generic constant")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
|
||||
pub(super) fn mir_abstract_const<'tcx>(
|
||||
pub(super) fn thir_abstract_const<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def: ty::WithOptConstParam<LocalDefId>,
|
||||
) -> Result<Option<&'tcx [mir::abstract_const::Node<'tcx>]>, ErrorReported> {
|
||||
) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorReported> {
|
||||
if tcx.features().generic_const_exprs {
|
||||
match tcx.def_kind(def.did) {
|
||||
// FIXME(generic_const_exprs): We currently only do this for anonymous constants,
|
||||
@ -547,8 +443,16 @@ pub(super) fn mir_abstract_const<'tcx>(
|
||||
DefKind::AnonConst => (),
|
||||
_ => return Ok(None),
|
||||
}
|
||||
let body = tcx.mir_const(def).borrow();
|
||||
AbstractConstBuilder::new(tcx, &body)?.map(AbstractConstBuilder::build).transpose()
|
||||
|
||||
let body = tcx.thir_body(def);
|
||||
if body.0.borrow().exprs.is_empty() {
|
||||
// type error in constant, there is no thir
|
||||
return Err(ErrorReported);
|
||||
}
|
||||
|
||||
AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))?
|
||||
.map(AbstractConstBuilder::build)
|
||||
.transpose()
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
@ -682,11 +586,16 @@ pub(super) fn try_unify<'tcx>(
|
||||
&& iter::zip(a_args, b_args)
|
||||
.all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn)))
|
||||
}
|
||||
(Node::Cast(a_cast_kind, a_operand, a_ty), Node::Cast(b_cast_kind, b_operand, b_ty))
|
||||
if (a_ty == b_ty) && (a_cast_kind == b_cast_kind) =>
|
||||
(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) =>
|
||||
{
|
||||
try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand))
|
||||
}
|
||||
_ => false,
|
||||
// use this over `_ => false` to make adding variants to `Node` less error prone
|
||||
(Node::Cast(..), _)
|
||||
| (Node::FunctionCall(..), _)
|
||||
| (Node::UnaryOp(..), _)
|
||||
| (Node::Binop(..), _)
|
||||
| (Node::Leaf(..), _) => false,
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::GenericParam;
|
||||
use rustc_hir::Item;
|
||||
use rustc_hir::Node;
|
||||
use rustc_middle::mir::abstract_const::NotConstEvaluatable;
|
||||
use rustc_middle::thir::abstract_const::NotConstEvaluatable;
|
||||
use rustc_middle::ty::error::ExpectedFound;
|
||||
use rustc_middle::ty::fold::TypeFolder;
|
||||
use rustc_middle::ty::{
|
||||
|
@ -5,8 +5,8 @@ use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProce
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::traits::{SelectionError, TraitEngine, TraitEngineExt as _, TraitObligation};
|
||||
use rustc_middle::mir::abstract_const::NotConstEvaluatable;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::thir::abstract_const::NotConstEvaluatable;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::subst::SubstsRef;
|
||||
use rustc_middle::ty::ToPredicate;
|
||||
|
@ -827,16 +827,16 @@ pub fn provide(providers: &mut ty::query::Providers) {
|
||||
vtable_entries,
|
||||
vtable_trait_upcasting_coercion_new_vptr_slot,
|
||||
subst_and_check_impossible_predicates,
|
||||
mir_abstract_const: |tcx, def_id| {
|
||||
thir_abstract_const: |tcx, def_id| {
|
||||
let def_id = def_id.expect_local();
|
||||
if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
|
||||
tcx.mir_abstract_const_of_const_arg(def)
|
||||
tcx.thir_abstract_const_of_const_arg(def)
|
||||
} else {
|
||||
const_evaluatable::mir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id))
|
||||
const_evaluatable::thir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id))
|
||||
}
|
||||
},
|
||||
mir_abstract_const_of_const_arg: |tcx, (did, param_did)| {
|
||||
const_evaluatable::mir_abstract_const(
|
||||
thir_abstract_const_of_const_arg: |tcx, (did, param_did)| {
|
||||
const_evaluatable::thir_abstract_const(
|
||||
tcx,
|
||||
ty::WithOptConstParam { did, const_param_did: Some(param_did) },
|
||||
)
|
||||
|
@ -836,7 +836,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>(
|
||||
//
|
||||
// This shouldn't really matter though as we can't really use any
|
||||
// constants which are not considered const evaluatable.
|
||||
use rustc_middle::mir::abstract_const::Node;
|
||||
use rustc_middle::thir::abstract_const::Node;
|
||||
if let Ok(Some(ct)) = AbstractConst::new(self.tcx, uv.shrink()) {
|
||||
const_evaluatable::walk_abstract_const(self.tcx, ct, |node| match node.root() {
|
||||
Node::Leaf(leaf) => {
|
||||
|
@ -34,8 +34,8 @@ use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::LateBoundRegionConversionTime;
|
||||
use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
|
||||
use rustc_middle::mir::abstract_const::NotConstEvaluatable;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::thir::abstract_const::NotConstEvaluatable;
|
||||
use rustc_middle::ty::fast_reject;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::relate::TypeRelation;
|
||||
|
@ -0,0 +1,11 @@
|
||||
#![feature(generic_const_exprs)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
fn foo<const N: u8>(a: [(); N as usize]) {
|
||||
bar::<{ N as usize as usize }>();
|
||||
//~^ error: unconstrained generic constant
|
||||
}
|
||||
|
||||
fn bar<const N: usize>() {}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,10 @@
|
||||
error: unconstrained generic constant
|
||||
--> $DIR/abstract-consts-as-cast-5.rs:5:11
|
||||
|
|
||||
LL | bar::<{ N as usize as usize }>();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: try adding a `where` bound using this expression: `where [(); { N as usize as usize }]:`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -10,7 +10,7 @@ error: overly complex generic constant
|
||||
--> $DIR/array-size-in-generic-struct-param.rs:19:15
|
||||
|
|
||||
LL | arr: [u8; CFG.arr_size],
|
||||
| ^^^^^^^^^^^^ unsupported projection
|
||||
| ^^^^^^^^^^^^ unsupported operation in generic constant, this may be supported in the future
|
||||
|
|
||||
= help: consider moving this anonymous constant into a `const` function
|
||||
|
||||
|
@ -4,7 +4,7 @@ error: overly complex generic constant
|
||||
LL | fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
|
||||
| ^^^^-------^^
|
||||
| |
|
||||
| unsupported rvalue
|
||||
| unsupported operation in generic constant, this may be supported in the future
|
||||
|
|
||||
= help: consider moving this anonymous constant into a `const` function
|
||||
|
||||
|
@ -2,9 +2,7 @@ error: overly complex generic constant
|
||||
--> $DIR/let-bindings.rs:6:68
|
||||
|
|
||||
LL | fn test<const N: usize>() -> [u8; { let x = N; N + 1 }] where [u8; { let x = N; N + 1 }]: Default {
|
||||
| ^^^^^^-^^^^^^^^^^^^^
|
||||
| |
|
||||
| unsupported statement
|
||||
| ^^^^^^^^^^^^^^^^^^^^ unsupported operation in generic constant, this may be supported in the future
|
||||
|
|
||||
= help: consider moving this anonymous constant into a `const` function
|
||||
|
||||
@ -12,9 +10,7 @@ error: overly complex generic constant
|
||||
--> $DIR/let-bindings.rs:6:35
|
||||
|
|
||||
LL | fn test<const N: usize>() -> [u8; { let x = N; N + 1 }] where [u8; { let x = N; N + 1 }]: Default {
|
||||
| ^^^^^^-^^^^^^^^^^^^^
|
||||
| |
|
||||
| unsupported statement
|
||||
| ^^^^^^^^^^^^^^^^^^^^ unsupported operation in generic constant, this may be supported in the future
|
||||
|
|
||||
= help: consider moving this anonymous constant into a `const` function
|
||||
|
||||
|
@ -0,0 +1,35 @@
|
||||
#![feature(generic_const_exprs, adt_const_params, const_trait_impl)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
// test `N + N` unifies with explicit function calls for non-builtin-types
|
||||
#[derive(PartialEq, Eq)]
|
||||
struct Foo(u8);
|
||||
|
||||
impl const std::ops::Add for Foo {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
struct Evaluatable<const N: Foo>;
|
||||
|
||||
fn foo<const N: Foo>(a: Evaluatable<{ N + N }>) {
|
||||
bar::<{ std::ops::Add::add(N, N) }>();
|
||||
}
|
||||
|
||||
fn bar<const N: Foo>() {}
|
||||
|
||||
// test that `N + N` unifies with explicit function calls for builin-types
|
||||
struct Evaluatable2<const N: usize>;
|
||||
|
||||
fn foo2<const N: usize>(a: Evaluatable2<{ N + N }>) {
|
||||
bar2::<{ std::ops::Add::add(N, N) }>();
|
||||
//~^ error: unconstrained generic constant
|
||||
// FIXME(generic_const_exprs) make this not an error
|
||||
}
|
||||
|
||||
fn bar2<const N: usize>() {}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,10 @@
|
||||
error: unconstrained generic constant
|
||||
--> $DIR/unify-op-with-fn-call.rs:28:12
|
||||
|
|
||||
LL | bar2::<{ std::ops::Add::add(N, N) }>();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: try adding a `where` bound using this expression: `where [(); { std::ops::Add::add(N, N) }]:`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -2,9 +2,7 @@ error: overly complex generic constant
|
||||
--> $DIR/unused_expr.rs:4:34
|
||||
|
|
||||
LL | fn add<const N: usize>() -> [u8; { N + 1; 5 }] {
|
||||
| ^^-----^^^^^
|
||||
| |
|
||||
| dead code
|
||||
| ^^^^^^^^^^^^ unsupported operation in generic constant, this may be supported in the future
|
||||
|
|
||||
= help: consider moving this anonymous constant into a `const` function
|
||||
|
||||
@ -12,9 +10,7 @@ error: overly complex generic constant
|
||||
--> $DIR/unused_expr.rs:9:34
|
||||
|
|
||||
LL | fn div<const N: usize>() -> [u8; { N / 1; 5 }] {
|
||||
| ^^-----^^^^^
|
||||
| |
|
||||
| dead code
|
||||
| ^^^^^^^^^^^^ unsupported operation in generic constant, this may be supported in the future
|
||||
|
|
||||
= help: consider moving this anonymous constant into a `const` function
|
||||
|
||||
@ -22,9 +18,7 @@ error: overly complex generic constant
|
||||
--> $DIR/unused_expr.rs:16:38
|
||||
|
|
||||
LL | fn fn_call<const N: usize>() -> [u8; { foo(N); 5 }] {
|
||||
| ^^------^^^^^
|
||||
| |
|
||||
| dead code
|
||||
| ^^^^^^^^^^^^^ unsupported operation in generic constant, this may be supported in the future
|
||||
|
|
||||
= help: consider moving this anonymous constant into a `const` function
|
||||
|
||||
|
@ -2,9 +2,9 @@ error: overly complex generic constant
|
||||
--> $DIR/issue-67375.rs:7:17
|
||||
|
|
||||
LL | inner: [(); { [|_: &T| {}; 0].len() }],
|
||||
| ^^^----------^^^^^^^^^^^^
|
||||
| |
|
||||
| unsupported rvalue
|
||||
| ^^---------------^^^^^^^^
|
||||
| |
|
||||
| unsupported operation in generic constant
|
||||
|
|
||||
= help: consider moving this anonymous constant into a `const` function
|
||||
|
||||
|
@ -5,11 +5,10 @@ LL | A: [(); {
|
||||
| _____________^
|
||||
LL | |
|
||||
LL | | let x: Option<Box<Self>> = None;
|
||||
| | ---- unsupported rvalue
|
||||
LL | |
|
||||
LL | | 0
|
||||
LL | | }],
|
||||
| |_____^
|
||||
| |_____^ unsupported operation in generic constant, this may be supported in the future
|
||||
|
|
||||
= help: consider moving this anonymous constant into a `const` function
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user