about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorEllen <supbscripter@gmail.com>2021-09-06 02:27:41 +0100
committerEllen <supbscripter@gmail.com>2021-09-09 01:32:03 +0100
commit2987f4ba42b002c58ae485f7ae528cb29c3b1611 (patch)
tree5e2b41f111b7a18e2fcd5dbc17a15bc8589d6fb7 /compiler
parent97032a6dfacdd3548e4bff98c90a6b3875a14077 (diff)
downloadrust-2987f4ba42b002c58ae485f7ae528cb29c3b1611.tar.gz
rust-2987f4ba42b002c58ae485f7ae528cb29c3b1611.zip
WIP state
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_middle/src/mir/abstract_const.rs1
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs8
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs3
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/mod.rs1
-rw-r--r--compiler/rustc_privacy/src/lib.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs360
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs7
7 files changed, 160 insertions, 227 deletions
diff --git a/compiler/rustc_middle/src/mir/abstract_const.rs b/compiler/rustc_middle/src/mir/abstract_const.rs
index 1ef10241143..e75f084e79e 100644
--- a/compiler/rustc_middle/src/mir/abstract_const.rs
+++ b/compiler/rustc_middle/src/mir/abstract_const.rs
@@ -17,6 +17,7 @@ pub enum Node<'tcx> {
     Binop(mir::BinOp, NodeId, NodeId),
     UnaryOp(mir::UnOp, NodeId),
     FunctionCall(NodeId, &'tcx [NodeId]),
+    Block(&'tcx [NodeId], Option<NodeId>),
     Cast(CastKind, NodeId, Ty<'tcx>),
 }
 
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 0a760a740dc..390ce59cb48 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -28,6 +28,7 @@ crate fn mir_built<'tcx>(
     if let Some(def) = def.try_upgrade(tcx) {
         return tcx.mir_built(def);
     }
+    debug!("mir_built: def={:?}", def);
 
     let mut body = mir_build(tcx, def);
     if def.const_param_did.is_some() {
@@ -40,6 +41,7 @@ crate fn mir_built<'tcx>(
 
 /// Construct the MIR for a given `DefId`.
 fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_> {
+    debug!("mir_build: def={:?}", def);
     let id = tcx.hir().local_def_id_to_hir_id(def.did);
     let body_owner_kind = tcx.hir().body_owner_kind(id);
     let typeck_results = tcx.typeck_opt_const_arg(def);
@@ -47,10 +49,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().mir_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().mir_abstract_const(did);
         }
     }
 
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 66005be05df..70a5a9286b0 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -149,7 +149,9 @@ impl<'tcx> Cx<'tcx> {
     }
 
     fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> {
+        debug!("Expr::make_mirror_unadjusted: expr={:?}", expr);
         let expr_ty = self.typeck_results().expr_ty(expr);
+        debug!("Expr::make_mirror_unadjusted: expr_ty={:?}", expr_ty);
         let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
 
         let kind = match expr.kind {
@@ -762,6 +764,7 @@ impl<'tcx> Cx<'tcx> {
             hir::ExprKind::Err => unreachable!(),
         };
 
+        debug!("Expr::make_mirror_unadjusted: finish");
         Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind }
     }
 
diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs
index 5059dd939d9..5310efbccd6 100644
--- a/compiler/rustc_mir_build/src/thir/cx/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs
@@ -20,6 +20,7 @@ crate fn thir_body<'tcx>(
     tcx: TyCtxt<'tcx>,
     owner_def: ty::WithOptConstParam<LocalDefId>,
 ) -> (&'tcx Steal<Thir<'tcx>>, ExprId) {
+    debug!("thir_body: {:?}", owner_def);
     let hir = tcx.hir();
     let body = hir.body(hir.body_owned_by(hir.local_def_id_to_hir_id(owner_def.did)));
     let mut cx = Cx::new(tcx, owner_def);
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 35e25e52dc5..26e1c3e7b8c 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -159,9 +159,10 @@ where
                 self.visit_const(leaf)
             }
             ACNode::Cast(_, _, ty) => self.visit_ty(ty),
-            ACNode::Binop(..) | ACNode::UnaryOp(..) | ACNode::FunctionCall(_, _) => {
-                ControlFlow::CONTINUE
-            }
+            ACNode::Block(_, _)
+            | ACNode::Binop(..)
+            | ACNode::UnaryOp(..)
+            | ACNode::FunctionCall(_, _) => ControlFlow::CONTINUE,
         })
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index ddabe5967d7..4e11fefdc81 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -8,14 +8,15 @@
 //! 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
 //! generic constants mentioned in the `caller_bounds` of the current environment.
+use rustc_data_structures::sync::Lrc;
 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;
 use rustc_middle::mir::abstract_const::{Node, NodeId, NotConstEvaluatable};
 use rustc_middle::mir::interpret::ErrorHandled;
-use rustc_middle::mir::{self, Rvalue, StatementKind, TerminatorKind};
+use rustc_middle::thir;
 use rustc_middle::ty::subst::{Subst, SubstsRef};
 use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
 use rustc_session::lint;
@@ -101,9 +102,10 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
 
                         ControlFlow::CONTINUE
                     }
-                    Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {
-                        ControlFlow::CONTINUE
-                    }
+                    Node::Block(_, _)
+                    | Node::Binop(_, _, _)
+                    | Node::UnaryOp(_, _)
+                    | Node::FunctionCall(_, _) => ControlFlow::CONTINUE,
                 });
 
                 match failure_kind {
@@ -232,7 +234,8 @@ struct WorkNode<'tcx> {
 
 struct AbstractConstBuilder<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    body: &'a mir::Body<'tcx>,
+    body_id: thir::ExprId,
+    body: Lrc<&'a thir::Thir<'tcx>>,
     /// The current WIP node tree.
     ///
     /// We require all nodes to be used in the final abstract const,
@@ -240,18 +243,18 @@ struct AbstractConstBuilder<'a, 'tcx> {
     /// 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>,
 }
 
 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,28 +263,12 @@ 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()),
-        };
-
-        // 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: Lrc::new(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")?;
-        }
+        // FIXME non-constants should return Ok(None)
 
         Ok(Some(builder))
     }
@@ -301,6 +288,10 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
                 self.nodes[func].used = true;
                 nodes.iter().for_each(|&n| self.nodes[n].used = true);
             }
+            Node::Block(stmts, opt_expr) => {
+                stmts.iter().for_each(|&id| self.nodes[id].used = true);
+                opt_expr.map(|e| self.nodes[e].used = true);
+            }
             Node::Cast(_, operand, _) => {
                 self.nodes[operand].used = true;
             }
@@ -310,50 +301,6 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
         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,148 +320,13 @@ 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)?;
-            }
-
-            if let Some(next) = self.build_terminator(block.terminator())? {
-                block = &self.body.basic_blocks()[next];
-            } else {
-                break;
-            }
-        }
+        debug!("Abstractconstbuilder::build: body={:?}", &*self.body);
+        let last = self.recurse_build(self.body_id)?;
+        self.nodes[last].used = true;
 
-        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 {
                 // `AbstractConst`s should not contain any promoteds as they require references which
@@ -523,13 +335,108 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
             }
         }
 
-        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().map(|n| n.node)))
     }
+
+    fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorReported> {
+        use thir::ExprKind;
+        let node = &self.body.clone().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)?,
+
+            &ExprKind::Literal { literal, .. }
+            | &ExprKind::StaticRef { literal, .. } => self.add_node(Node::Leaf(literal), node.span),
+
+            // FIXME(generic_const_exprs) handle `from_hir_call` field
+            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.add_node(Node::FunctionCall(fun, new_args), node.span)
+            },
+            &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => {
+                let lhs = self.recurse_build(lhs)?;
+                let rhs = self.recurse_build(rhs)?;
+                self.add_node(Node::Binop(op, lhs, rhs), node.span)
+            }
+            &ExprKind::Unary { op, arg } if Self::check_unop(op) => {
+                let arg = self.recurse_build(arg)?;
+                self.add_node(Node::UnaryOp(op, arg), node.span)
+            },
+            // HACK: without this arm the following doesn't compile:
+            // ```
+            // fn foo<const N: usize>(_: [(); N + 1]) {
+            //     bar::<{ N + 1}>();
+            // }
+            // ```
+            // we ought to properly handle this in `try_unify`
+            ExprKind::Block { body: thir::Block { stmts: box [], expr: Some(e), .. }} => self.recurse_build(*e)?,
+            ExprKind::Block { body } => {
+                let mut stmts = Vec::with_capacity(body.stmts.len());
+                for &id in body.stmts.iter() {
+                    match &self.body.stmts[id].kind {
+                        thir::StmtKind::Let { .. } => return self.error(
+                                Some(node.span),
+                                "let statements are not supported in generic constants",
+                            ).map(|never| never),
+                        thir::StmtKind::Expr { expr, .. } => stmts.push(self.recurse_build(*expr)?),
+                    }
+                };
+                let stmts = self.tcx.arena.alloc_slice(&stmts);
+                let opt_expr = body.expr.map(|e| self.recurse_build(e)).transpose()?;
+                self.add_node(Node::Block(stmts, opt_expr), node.span)
+            }
+            &ExprKind::Cast { source } => todo!(),
+            // never can arise even without panic/fail to terminate
+            &ExprKind::NeverToAny { source } => todo!(),
+            // i think this is a dummy usage of the expr to allow coercions
+            &ExprKind::Use { source } => todo!(),
+
+            ExprKind::Return { .. }
+            | ExprKind::Box { .. } // allocations not allowed in constants
+            | ExprKind::AssignOp { .. }
+            | ExprKind::AddressOf { .. } // FIXME(generic_const_exprs)
+            | ExprKind::Borrow { .. } // FIXME(generic_const_exprs)
+            | ExprKind::Deref { .. } // FIXME(generic_const_exprs)
+            | ExprKind::Repeat { .. } // FIXME(generic_const_exprs)
+            | ExprKind::Array { .. } // FIXME(generic_const_exprs)
+            | ExprKind::Tuple { .. } // FIXME(generic_const_exprs)
+            | ExprKind::Index { .. } // FIXME(generic_const_exprs)
+            | ExprKind::Field { .. } // FIXME(generic_const_exprs)
+            | ExprKind::ConstBlock { .. } // FIXME(generic_const_exprs)
+            | ExprKind::Adt(_) // FIXME(generic_const_exprs) we *should* permit this but dont currently
+            | ExprKind::Match { .. }
+            | ExprKind::VarRef { .. } //
+            | ExprKind::UpvarRef { .. } // we dont permit let stmts so...
+            | ExprKind::Closure { .. }
+            | ExprKind::Let { .. } // let expressions imply control flow
+            | ExprKind::Loop { .. }
+            | ExprKind::Assign { .. }
+            | ExprKind::LogicalOp { .. }
+            | ExprKind::Unary { .. } //
+            | ExprKind::Binary { .. } // we handle valid unary/binary ops above 
+            | ExprKind::Break { .. }
+            | ExprKind::Continue { .. }
+            | ExprKind::If { .. }
+            | ExprKind::Pointer { .. } // dont know if this is correct
+            | ExprKind::ThreadLocalRef(_)
+            | ExprKind::LlvmInlineAsm { .. }
+            | ExprKind::InlineAsm { .. }
+            | ExprKind::Yield { .. } => return self.error(Some(node.span), "unsupported operation in generic constant").map(|never| never),
+        })
+    }
 }
 
 /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
@@ -547,8 +454,17 @@ 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()
+        debug!("mir_abstract_const: {:?}", def);
+        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)
     }
@@ -599,6 +515,12 @@ where
                 recurse(tcx, ct.subtree(func), f)?;
                 args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f))
             }
+            Node::Block(stmts, opt_expr) => {
+                for id in stmts.iter().copied().chain(opt_expr) {
+                    recurse(tcx, ct.subtree(id), f)?;
+                }
+                ControlFlow::CONTINUE
+            }
             Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f),
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 57b8a84300f..e64cc9e4b8f 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -844,9 +844,10 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>(
                         self.visit_const(leaf)
                     }
                     Node::Cast(_, _, ty) => self.visit_ty(ty),
-                    Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => {
-                        ControlFlow::CONTINUE
-                    }
+                    Node::Block(_, _)
+                    | Node::Binop(..)
+                    | Node::UnaryOp(..)
+                    | Node::FunctionCall(_, _) => ControlFlow::CONTINUE,
                 })
             } else {
                 ControlFlow::CONTINUE