frontend 0.4.0

rustc's frontend with no LLVM and no std: parsing through MIR, as a library
// `#![no_std]`: these arrive with the standard prelude and name no path, so a `std::`
// search cannot see them - and a `#[derive]` can use them without the name appearing
// in this file at all, which is why they are not trimmed by inspection.
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;

use core::ops::Deref;

use crate::rustc_hir::def::DefKind;
use crate::rustc_hir::def_id::LocalDefId;
use crate::rustc_middle::mir::{
    Body, Const, ConstValue, Operand, Place, RETURN_PLACE, Rvalue, START_BLOCK, StatementKind,
    TerminatorKind, UnevaluatedConst,
};
use crate::rustc_middle::ty::{AnonConstKind, Ty, TyCtxt, TypeVisitableExt};

/// If the given def is a trivial const, returns the value and type the const evaluates to.
///
/// A "trivial const" is a const which can be easily proven to evaluate successfully, and the value
/// that it evaluates to can be easily found without going through the usual MIR phases for a const.
///
/// Currently, we support two forms of trivial const.
///
/// The base case is this:
/// ```
/// const A: usize = 0;
/// ```
/// which has this MIR:
/// ```text
/// const A: usize = {
///     let mut _0: usize;
///
///     bb0: {
///         _0 = const 0_usize;
///         return;
///     }
/// }
/// ```
/// Which we recognize by looking for a Body which has a single basic block with a return
/// terminator and a single statement which assigns an `Operand::Constant(Const::Val)` to the
/// return place.
/// This scenario meets the required criteria because:
/// * Control flow cannot panic, we don't have any calls or assert terminators
/// * The value of the const is already computed, so it cannot fail
///
/// In addition to assignment of literals, assignments of trivial consts are also considered
/// trivial consts. In this case, both `A` and `B` are trivial:
/// ```
/// const A: usize = 0;
/// const B: usize = A;
/// ```
pub(crate) fn trivial_const<'a, 'tcx: 'a, F, B>(
    tcx: TyCtxt<'tcx>,
    def: LocalDefId,
    body_provider: F,
) -> Option<(ConstValue, Ty<'tcx>)>
where
    F: FnOnce() -> B,
    B: Deref<Target = Body<'tcx>>,
{
    match tcx.def_kind(def) {
        DefKind::AssocConst { .. } | DefKind::Const { .. } => (),
        DefKind::AnonConst if tcx.anon_const_kind(def) != AnonConstKind::NonTypeSystemInline => (),
        _ => return None,
    }

    // If there are impossible clauses then MIR passes will replace the body with
    // `unreachable` causing const eval errors when trying to evaluate the body. For
    // now we avoid using trivial consts for such bodies so that the behaviour doesn't
    // change.
    if crate::rustc_mir_transform::impossible_clauses::has_impossible_clauses(tcx, def.into()) {
        return None;
    }

    if !tcx.opaque_types_defined_by(def).is_empty() {
        return None;
    }

    let body = body_provider();

    if body.has_opaque_types() {
        return None;
    }

    if body.basic_blocks.len() != 1 {
        return None;
    }

    let block = &body.basic_blocks[START_BLOCK];
    if block.statements.len() != 1 {
        return None;
    }

    if block.terminator().kind != TerminatorKind::Return {
        return None;
    }

    let StatementKind::Assign(assign) = &block.statements[0].kind else {
        return None;
    };
    let (place, rvalue) = &**assign;

    if *place != Place::from(RETURN_PLACE) {
        return None;
    }

    let Rvalue::Use(Operand::Constant(c), _) = rvalue else {
        return None;
    };
    match c.const_ {
        Const::Ty(..) => None,
        Const::Unevaluated(UnevaluatedConst { def, args, .. }, _ty) => {
            if !args.is_empty() {
                return None;
            }
            tcx.trivial_const(def)
        }
        Const::Val(v, ty) => Some((v, ty)),
    }
}

// The query provider is based on calling the free function trivial_const, which calls mir_built,
// which internally has a fast-path for trivial consts so it too calls trivial_const. This isn't
// recursive, but we are checking if the const is trivial twice. A better design might detect
// trivial consts before getting to MIR, which would hopefully straighten this out.
pub(crate) fn trivial_const_provider<'tcx>(
    tcx: TyCtxt<'tcx>,
    def: LocalDefId,
) -> Option<(ConstValue, Ty<'tcx>)> {
    trivial_const(tcx, def, || tcx.mir_built(def).borrow())
}