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};
}
}
-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)
}
.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)))))
}
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) }
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};
}
}
-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)
}
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));
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;
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
+++ /dev/null
-//! 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,
-}
use self::predecessors::{PredecessorCache, Predecessors};
pub use self::query::*;
-pub mod abstract_const;
pub mod coverage;
mod generic_graph;
pub mod generic_graphviz;
//! 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;
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)
- }
- }
}
}
/// 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 {}",
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)]
--- /dev/null
+//! 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)
+ }
+ }
+}
--- /dev/null
+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 {
+ fn thir(&self) -> &'a Thir<'tcx>;
+
+ fn visit_expr(&mut self, expr: &Expr<'tcx>) {
+ walk_expr(self, expr);
+ }
+
+ fn visit_stmt(&mut self, stmt: &Stmt<'tcx>) {
+ walk_stmt(self, stmt);
+ }
+
+ fn visit_block(&mut self, block: &Block) {
+ walk_block(self, block);
+ }
+
+ fn visit_arm(&mut self, arm: &Arm<'tcx>) {
+ walk_arm(self, arm);
+ }
+
+ fn visit_pat(&mut self, pat: &Pat<'tcx>) {
+ walk_pat(self, pat);
+ }
+
+ fn visit_const(&mut self, _cnst: &'tcx Const<'tcx>) {}
+}
+
+pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Expr<'tcx>) {
+ use ExprKind::*;
+ match expr.kind {
+ Scope { value, region_scope: _, lint_level: _ } => {
+ visitor.visit_expr(&visitor.thir()[value])
+ }
+ Box { value } => visitor.visit_expr(&visitor.thir()[value]),
+ If { cond, then, else_opt, if_then_scope: _ } => {
+ visitor.visit_expr(&visitor.thir()[cond]);
+ visitor.visit_expr(&visitor.thir()[then]);
+ if let Some(else_expr) = else_opt {
+ visitor.visit_expr(&visitor.thir()[else_expr]);
+ }
+ }
+ Call { fun, ref args, ty: _, from_hir_call: _, fn_span: _ } => {
+ visitor.visit_expr(&visitor.thir()[fun]);
+ for &arg in &**args {
+ visitor.visit_expr(&visitor.thir()[arg]);
+ }
+ }
+ Deref { arg } => visitor.visit_expr(&visitor.thir()[arg]),
+ Binary { lhs, rhs, op: _ } | LogicalOp { lhs, rhs, op: _ } => {
+ visitor.visit_expr(&visitor.thir()[lhs]);
+ visitor.visit_expr(&visitor.thir()[rhs]);
+ }
+ Unary { arg, op: _ } => visitor.visit_expr(&visitor.thir()[arg]),
+ Cast { source } => visitor.visit_expr(&visitor.thir()[source]),
+ Use { source } => visitor.visit_expr(&visitor.thir()[source]),
+ NeverToAny { source } => visitor.visit_expr(&visitor.thir()[source]),
+ Pointer { source, cast: _ } => visitor.visit_expr(&visitor.thir()[source]),
+ Let { expr, .. } => {
+ visitor.visit_expr(&visitor.thir()[expr]);
+ }
+ Loop { body } => visitor.visit_expr(&visitor.thir()[body]),
+ Match { scrutinee, ref arms } => {
+ visitor.visit_expr(&visitor.thir()[scrutinee]);
+ for &arm in &**arms {
+ visitor.visit_arm(&visitor.thir()[arm]);
+ }
+ }
+ Block { ref body } => visitor.visit_block(body),
+ Assign { lhs, rhs } | AssignOp { lhs, rhs, op: _ } => {
+ visitor.visit_expr(&visitor.thir()[lhs]);
+ visitor.visit_expr(&visitor.thir()[rhs]);
+ }
+ Field { lhs, name: _ } => visitor.visit_expr(&visitor.thir()[lhs]),
+ Index { lhs, index } => {
+ visitor.visit_expr(&visitor.thir()[lhs]);
+ visitor.visit_expr(&visitor.thir()[index]);
+ }
+ VarRef { id: _ } | UpvarRef { closure_def_id: _, var_hir_id: _ } => {}
+ Borrow { arg, borrow_kind: _ } => visitor.visit_expr(&visitor.thir()[arg]),
+ AddressOf { arg, mutability: _ } => visitor.visit_expr(&visitor.thir()[arg]),
+ Break { value, label: _ } => {
+ if let Some(value) = value {
+ visitor.visit_expr(&visitor.thir()[value])
+ }
+ }
+ Continue { label: _ } => {}
+ Return { value } => {
+ if let Some(value) = value {
+ visitor.visit_expr(&visitor.thir()[value])
+ }
+ }
+ ConstBlock { value } => visitor.visit_const(value),
+ Repeat { value, count } => {
+ visitor.visit_expr(&visitor.thir()[value]);
+ visitor.visit_const(count);
+ }
+ Array { ref fields } | Tuple { ref fields } => {
+ for &field in &**fields {
+ visitor.visit_expr(&visitor.thir()[field]);
+ }
+ }
+ Adt(box crate::thir::Adt {
+ ref fields,
+ ref base,
+ adt_def: _,
+ variant_index: _,
+ substs: _,
+ user_ty: _,
+ }) => {
+ for field in &**fields {
+ visitor.visit_expr(&visitor.thir()[field.expr]);
+ }
+ if let Some(base) = base {
+ visitor.visit_expr(&visitor.thir()[base.base]);
+ }
+ }
+ PlaceTypeAscription { source, user_ty: _ } | ValueTypeAscription { source, user_ty: _ } => {
+ visitor.visit_expr(&visitor.thir()[source])
+ }
+ Closure { closure_id: _, substs: _, upvars: _, movability: _, fake_reads: _ } => {}
+ Literal { literal, user_ty: _, const_id: _ } => visitor.visit_const(literal),
+ StaticRef { literal, def_id: _ } => visitor.visit_const(literal),
+ InlineAsm { ref operands, template: _, options: _, line_spans: _ } => {
+ for op in &**operands {
+ use InlineAsmOperand::*;
+ match op {
+ In { expr, reg: _ }
+ | Out { expr: Some(expr), reg: _, late: _ }
+ | InOut { expr, reg: _, late: _ }
+ | SymFn { expr } => visitor.visit_expr(&visitor.thir()[*expr]),
+ SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
+ visitor.visit_expr(&visitor.thir()[*in_expr]);
+ if let Some(out_expr) = out_expr {
+ visitor.visit_expr(&visitor.thir()[*out_expr]);
+ }
+ }
+ Out { expr: None, reg: _, late: _ }
+ | Const { value: _, span: _ }
+ | SymStatic { def_id: _ } => {}
+ }
+ }
+ }
+ ThreadLocalRef(_) => {}
+ LlvmInlineAsm { ref outputs, ref inputs, asm: _ } => {
+ for &out_expr in &**outputs {
+ visitor.visit_expr(&visitor.thir()[out_expr]);
+ }
+ for &in_expr in &**inputs {
+ visitor.visit_expr(&visitor.thir()[in_expr]);
+ }
+ }
+ Yield { value } => visitor.visit_expr(&visitor.thir()[value]),
+ }
+}
+
+pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stmt<'tcx>) {
+ match &stmt.kind {
+ StmtKind::Expr { expr, scope: _ } => visitor.visit_expr(&visitor.thir()[*expr]),
+ StmtKind::Let {
+ initializer,
+ remainder_scope: _,
+ init_scope: _,
+ ref pattern,
+ lint_level: _,
+ } => {
+ if let Some(init) = initializer {
+ visitor.visit_expr(&visitor.thir()[*init]);
+ }
+ visitor.visit_pat(pattern);
+ }
+ }
+}
+
+pub fn walk_block<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, block: &Block) {
+ for &stmt in &*block.stmts {
+ visitor.visit_stmt(&visitor.thir()[stmt]);
+ }
+ if let Some(expr) = block.expr {
+ visitor.visit_expr(&visitor.thir()[expr]);
+ }
+}
+
+pub fn walk_arm<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, arm: &Arm<'tcx>) {
+ match arm.guard {
+ Some(Guard::If(expr)) => visitor.visit_expr(&visitor.thir()[expr]),
+ Some(Guard::IfLet(ref pat, expr)) => {
+ visitor.visit_pat(pat);
+ visitor.visit_expr(&visitor.thir()[expr]);
+ }
+ None => {}
+ }
+ visitor.visit_pat(&arm.pattern);
+ visitor.visit_expr(&visitor.thir()[arm.body]);
+}
+
+pub fn walk_pat<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, pat: &Pat<'tcx>) {
+ use PatKind::*;
+ match pat.kind.as_ref() {
+ AscribeUserType { subpattern, ascription: _ }
+ | Deref { subpattern }
+ | Binding {
+ subpattern: Some(subpattern),
+ mutability: _,
+ mode: _,
+ var: _,
+ ty: _,
+ is_primary: _,
+ name: _,
+ } => visitor.visit_pat(&subpattern),
+ Binding { .. } | Wild => {}
+ Variant { subpatterns, adt_def: _, substs: _, variant_index: _ } | Leaf { subpatterns } => {
+ for subpattern in subpatterns {
+ visitor.visit_pat(&subpattern.pattern);
+ }
+ }
+ Constant { value } => visitor.visit_const(value),
+ Range(range) => {
+ visitor.visit_const(range.lo);
+ visitor.visit_const(range.hi);
+ }
+ Slice { prefix, slice, suffix } | Array { prefix, slice, suffix } => {
+ for subpattern in prefix {
+ visitor.visit_pat(&subpattern);
+ }
+ if let Some(pat) = slice {
+ visitor.visit_pat(pat);
+ }
+ for subpattern in suffix {
+ visitor.visit_pat(&subpattern);
+ }
+ }
+ Or { pats } => {
+ for pat in pats {
+ visitor.visit_pat(&pat);
+ }
+ }
+ };
+}
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};
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;
}
}
-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()?)
}
}
-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()?)
// 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);
}
}
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;
crate mod pattern;
mod util;
-pub mod visit;
+++ /dev/null
-use rustc_middle::thir::{self, *};
-use rustc_middle::ty::Const;
-
-pub trait Visitor<'a, 'tcx: 'a>: Sized {
- fn thir(&self) -> &'a Thir<'tcx>;
-
- fn visit_expr(&mut self, expr: &Expr<'tcx>) {
- walk_expr(self, expr);
- }
-
- fn visit_stmt(&mut self, stmt: &Stmt<'tcx>) {
- walk_stmt(self, stmt);
- }
-
- fn visit_block(&mut self, block: &Block) {
- walk_block(self, block);
- }
-
- fn visit_arm(&mut self, arm: &Arm<'tcx>) {
- walk_arm(self, arm);
- }
-
- fn visit_pat(&mut self, pat: &Pat<'tcx>) {
- walk_pat(self, pat);
- }
-
- fn visit_const(&mut self, _cnst: &'tcx Const<'tcx>) {}
-}
-
-pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Expr<'tcx>) {
- use ExprKind::*;
- match expr.kind {
- Scope { value, region_scope: _, lint_level: _ } => {
- visitor.visit_expr(&visitor.thir()[value])
- }
- Box { value } => visitor.visit_expr(&visitor.thir()[value]),
- If { cond, then, else_opt, if_then_scope: _ } => {
- visitor.visit_expr(&visitor.thir()[cond]);
- visitor.visit_expr(&visitor.thir()[then]);
- if let Some(else_expr) = else_opt {
- visitor.visit_expr(&visitor.thir()[else_expr]);
- }
- }
- Call { fun, ref args, ty: _, from_hir_call: _, fn_span: _ } => {
- visitor.visit_expr(&visitor.thir()[fun]);
- for &arg in &**args {
- visitor.visit_expr(&visitor.thir()[arg]);
- }
- }
- Deref { arg } => visitor.visit_expr(&visitor.thir()[arg]),
- Binary { lhs, rhs, op: _ } | LogicalOp { lhs, rhs, op: _ } => {
- visitor.visit_expr(&visitor.thir()[lhs]);
- visitor.visit_expr(&visitor.thir()[rhs]);
- }
- Unary { arg, op: _ } => visitor.visit_expr(&visitor.thir()[arg]),
- Cast { source } => visitor.visit_expr(&visitor.thir()[source]),
- Use { source } => visitor.visit_expr(&visitor.thir()[source]),
- NeverToAny { source } => visitor.visit_expr(&visitor.thir()[source]),
- Pointer { source, cast: _ } => visitor.visit_expr(&visitor.thir()[source]),
- Let { expr, .. } => {
- visitor.visit_expr(&visitor.thir()[expr]);
- }
- Loop { body } => visitor.visit_expr(&visitor.thir()[body]),
- Match { scrutinee, ref arms } => {
- visitor.visit_expr(&visitor.thir()[scrutinee]);
- for &arm in &**arms {
- visitor.visit_arm(&visitor.thir()[arm]);
- }
- }
- Block { ref body } => visitor.visit_block(body),
- Assign { lhs, rhs } | AssignOp { lhs, rhs, op: _ } => {
- visitor.visit_expr(&visitor.thir()[lhs]);
- visitor.visit_expr(&visitor.thir()[rhs]);
- }
- Field { lhs, name: _ } => visitor.visit_expr(&visitor.thir()[lhs]),
- Index { lhs, index } => {
- visitor.visit_expr(&visitor.thir()[lhs]);
- visitor.visit_expr(&visitor.thir()[index]);
- }
- VarRef { id: _ } | UpvarRef { closure_def_id: _, var_hir_id: _ } => {}
- Borrow { arg, borrow_kind: _ } => visitor.visit_expr(&visitor.thir()[arg]),
- AddressOf { arg, mutability: _ } => visitor.visit_expr(&visitor.thir()[arg]),
- Break { value, label: _ } => {
- if let Some(value) = value {
- visitor.visit_expr(&visitor.thir()[value])
- }
- }
- Continue { label: _ } => {}
- Return { value } => {
- if let Some(value) = value {
- visitor.visit_expr(&visitor.thir()[value])
- }
- }
- ConstBlock { value } => visitor.visit_const(value),
- Repeat { value, count } => {
- visitor.visit_expr(&visitor.thir()[value]);
- visitor.visit_const(count);
- }
- Array { ref fields } | Tuple { ref fields } => {
- for &field in &**fields {
- visitor.visit_expr(&visitor.thir()[field]);
- }
- }
- Adt(box thir::Adt {
- ref fields,
- ref base,
- adt_def: _,
- variant_index: _,
- substs: _,
- user_ty: _,
- }) => {
- for field in &**fields {
- visitor.visit_expr(&visitor.thir()[field.expr]);
- }
- if let Some(base) = base {
- visitor.visit_expr(&visitor.thir()[base.base]);
- }
- }
- PlaceTypeAscription { source, user_ty: _ } | ValueTypeAscription { source, user_ty: _ } => {
- visitor.visit_expr(&visitor.thir()[source])
- }
- Closure { closure_id: _, substs: _, upvars: _, movability: _, fake_reads: _ } => {}
- Literal { literal, user_ty: _, const_id: _ } => visitor.visit_const(literal),
- StaticRef { literal, def_id: _ } => visitor.visit_const(literal),
- InlineAsm { ref operands, template: _, options: _, line_spans: _ } => {
- for op in &**operands {
- use InlineAsmOperand::*;
- match op {
- In { expr, reg: _ }
- | Out { expr: Some(expr), reg: _, late: _ }
- | InOut { expr, reg: _, late: _ }
- | SymFn { expr } => visitor.visit_expr(&visitor.thir()[*expr]),
- SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
- visitor.visit_expr(&visitor.thir()[*in_expr]);
- if let Some(out_expr) = out_expr {
- visitor.visit_expr(&visitor.thir()[*out_expr]);
- }
- }
- Out { expr: None, reg: _, late: _ }
- | Const { value: _, span: _ }
- | SymStatic { def_id: _ } => {}
- }
- }
- }
- ThreadLocalRef(_) => {}
- LlvmInlineAsm { ref outputs, ref inputs, asm: _ } => {
- for &out_expr in &**outputs {
- visitor.visit_expr(&visitor.thir()[out_expr]);
- }
- for &in_expr in &**inputs {
- visitor.visit_expr(&visitor.thir()[in_expr]);
- }
- }
- Yield { value } => visitor.visit_expr(&visitor.thir()[value]),
- }
-}
-
-pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stmt<'tcx>) {
- match &stmt.kind {
- StmtKind::Expr { expr, scope: _ } => visitor.visit_expr(&visitor.thir()[*expr]),
- StmtKind::Let {
- initializer,
- remainder_scope: _,
- init_scope: _,
- ref pattern,
- lint_level: _,
- } => {
- if let Some(init) = initializer {
- visitor.visit_expr(&visitor.thir()[*init]);
- }
- visitor.visit_pat(pattern);
- }
- }
-}
-
-pub fn walk_block<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, block: &Block) {
- for &stmt in &*block.stmts {
- visitor.visit_stmt(&visitor.thir()[stmt]);
- }
- if let Some(expr) = block.expr {
- visitor.visit_expr(&visitor.thir()[expr]);
- }
-}
-
-pub fn walk_arm<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, arm: &Arm<'tcx>) {
- match arm.guard {
- Some(Guard::If(expr)) => visitor.visit_expr(&visitor.thir()[expr]),
- Some(Guard::IfLet(ref pat, expr)) => {
- visitor.visit_pat(pat);
- visitor.visit_expr(&visitor.thir()[expr]);
- }
- None => {}
- }
- visitor.visit_pat(&arm.pattern);
- visitor.visit_expr(&visitor.thir()[arm.body]);
-}
-
-pub fn walk_pat<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, pat: &Pat<'tcx>) {
- use PatKind::*;
- match pat.kind.as_ref() {
- AscribeUserType { subpattern, ascription: _ }
- | Deref { subpattern }
- | Binding {
- subpattern: Some(subpattern),
- mutability: _,
- mode: _,
- var: _,
- ty: _,
- is_primary: _,
- name: _,
- } => visitor.visit_pat(&subpattern),
- Binding { .. } | Wild => {}
- Variant { subpatterns, adt_def: _, substs: _, variant_index: _ } | Leaf { subpatterns } => {
- for subpattern in subpatterns {
- visitor.visit_pat(&subpattern.pattern);
- }
- }
- Constant { value } => visitor.visit_const(value),
- Range(range) => {
- visitor.visit_const(range.lo);
- visitor.visit_const(range.hi);
- }
- Slice { prefix, slice, suffix } | Array { prefix, slice, suffix } => {
- for subpattern in prefix {
- visitor.visit_pat(&subpattern);
- }
- if let Some(pat) = slice {
- visitor.visit_pat(pat);
- }
- for subpattern in suffix {
- visitor.visit_pat(&subpattern);
- }
- }
- Or { pats } => {
- for pat in pats {
- visitor.visit_pat(&pat);
- }
- }
- };
-}
// 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();
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};
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;
}
}
-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)
}
//! 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;
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) }))
}
}
}
-#[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();
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()),
- };
-
- // We don't have to look at concrete constants, as we
- // can just evaluate them.
- if !body.is_polymorphic {
- return Ok(None);
- }
+ let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() };
- // 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")?;
+ struct IsThirPolymorphic<'a, 'tcx> {
+ is_poly: bool,
+ thir: &'a thir::Thir<'tcx>,
+ tcx: TyCtxt<'tcx>,
}
- 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);
+ use thir::visit;
+ impl<'a, 'tcx: 'a> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
+ fn thir(&self) -> &'a thir::Thir<'tcx> {
+ &self.thir
}
- Node::Cast(_, operand, _) => {
- self.nodes[operand].used = true;
+
+ 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)
+ }
}
- }
- // Nodes start as unused.
- self.nodes.push(WorkNode { node, span, used: false })
- }
+ 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 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")?;
+ fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) {
+ self.is_poly |= ct.definitely_has_param_types_or_consts(self.tcx);
}
- } 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")?,
- },
+ 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);
}
+
+ Ok(Some(builder))
}
/// We do not allow all binary operations in abstract consts, so filter disallowed ones.
}
}
- 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,
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)
}
&& 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,
}
}
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::{
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;
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) },
)
//
// 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) => {
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;
--- /dev/null
+#![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() {}
--- /dev/null
+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
+
--> $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
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
--> $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
--> $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
--- /dev/null
+#![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() {}
--- /dev/null
+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
+
--> $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
--> $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
--> $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
--> $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
| _____________^
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