1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
//! 对 libcore 的 panic 支持
//!
//! 核心库无法定义 panic,但可以声明 panic。
//! 这意味着 libcore 内部的函数被允许用于 panic,但上游 crate 有用时必须定义 panic 以供 libcore 使用。
//! 当前的 panic 接口是:
//!
//! ```
//! fn panic_impl(pi: &core::panic::PanicInfo<'_>) -> !
//! # { loop {} }
//! ```
//!
//! 此定义允许对任何常规消息进行 panic,但不允许 `Box<Any>` 值失败。
//! (`PanicInfo` 仅包含一个 `&(dyn Any + Send)`,我们在其中将其填充为 `PanicInfo::internal_constructor` 中的虚拟值。) 其原因是不允许 libcore 进行分配。
//!
//!
//! 该模块还包含其他一些紧急函数,但这只是编译器必需的 lang 项。所有 panics 都通过此函数进行了分配。
//! 实际符号通过 `#[panic_handler]` 属性声明。
//!
//!
//!

#![allow(dead_code, missing_docs)]
#![unstable(
    feature = "core_panic",
    reason = "internal details of the implementation of the `panic!` and related macros",
    issue = "none"
)]

use crate::fmt;
use crate::panic::{Location, PanicInfo};

/// 不使用格式时,libcore 的 `panic!` 宏的底层实现。
#[cold]
// 从不内联,除非 panic_immediate_abort 尽可能避免代码在调用站点上膨胀
//
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[rustc_const_unstable(feature = "core_panic", issue = "none")]
#[lang = "panic"] // 溢出和其他 `Assert` MIR 终结器时 panic 的代码生成所需的
pub const fn panic(expr: &'static str) -> ! {
    // 使用 Arguments::new_v1 代替 format_args! (`{}`,expr) 可能会减少大小开销。
    // The format_args!  宏使用 str 的 Display trait 来编写 expr,它调用 Formatter::pad,它必须适应字符串截断和填充 (尽管这里没有使用任何内容)。
    //
    // 使用 Arguments::new_v1 可使编译器从输出二进制文件中省略 Formatter::pad,从而节省多达几千字节的空间。
    //
    //
    panic_fmt(fmt::Arguments::new_v1(&[expr], &[]));
}

#[inline]
#[track_caller]
#[rustc_diagnostic_item = "panic_str"]
#[rustc_const_unstable(feature = "core_panic", issue = "none")]
pub const fn panic_str(expr: &str) -> ! {
    panic_display(&expr);
}

#[cfg(not(bootstrap))]
#[inline]
#[track_caller]
#[rustc_diagnostic_item = "unreachable_display"] // `non-fmt-panics` lint 需要
pub fn unreachable_display<T: fmt::Display>(x: &T) -> ! {
    panic_fmt(format_args!("internal error: entered unreachable code: {}", *x));
}

#[inline]
#[track_caller]
#[lang = "panic_display"] // 常量评估的 panics 所需
#[rustc_do_not_const_check] // 被 const-eval 钩住了
pub const fn panic_display<T: fmt::Display>(x: &T) -> ! {
    panic_fmt(format_args!("{}", *x));
}

#[cold]
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
#[track_caller]
#[lang = "panic_bounds_check"] // OOB array/slice 访问上 panic 的代码生成所需的
fn panic_bounds_check(index: usize, len: usize) -> ! {
    if cfg!(feature = "panic_immediate_abort") {
        super::intrinsics::abort()
    }

    panic!("index out of bounds: the len is {} but the index is {}", len, index)
}

/// 带有格式化消息的 panic 的入口点。
///
/// 通过将实际格式移到该共享位置,可以最大程度地减少调用站点上所需的代码量 (以使 `panic!()` 对 (e.g.) 内联其他函数的影响尽可能小)。
///
///
///
#[cold]
// 如果使用 panic_immediate_abort,则内联终止调用,否则请避免内联,因为它是冷路径。
//
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[lang = "panic_fmt"] // 常量评估的 panics 所需
#[rustc_do_not_const_check] // 被 const-eval 钩住了
pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
    if cfg!(feature = "panic_immediate_abort") {
        super::intrinsics::abort()
    }

    // NOTE 这个函数永远不会越过 FFI 边界。这是 Rust 到 Rust 调用,已解析为 `#[panic_handler]` 函数。
    //
    extern "Rust" {
        #[lang = "panic_impl"]
        fn panic_impl(pi: &PanicInfo<'_>) -> !;
    }

    let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller());

    // SAFETY: `panic_impl` 在安全的 Rust 代码中定义,因此可以安全调用。
    unsafe { panic_impl(&pi) }
}

/// 在 const eval 中使用此函数代替 panic_fmt。
#[lang = "const_panic_fmt"]
pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
    if let Some(msg) = fmt.as_str() {
        panic_str(msg);
    } else {
        // SAFETY: 这仅在编译时评估,它可靠地处理此 UB (以防此分支以某种方式可达)。
        //
        //
        unsafe { crate::hint::unreachable_unchecked() };
    }
}

#[derive(Debug)]
#[doc(hidden)]
pub enum AssertKind {
    Eq,
    Ne,
    Match,
}

/// `assert_eq!` 和 `assert_ne!` 宏的内联函数
#[cold]
#[track_caller]
#[doc(hidden)]
pub fn assert_failed<T, U>(
    kind: AssertKind,
    left: &T,
    right: &U,
    args: Option<fmt::Arguments<'_>>,
) -> !
where
    T: fmt::Debug + ?Sized,
    U: fmt::Debug + ?Sized,
{
    assert_failed_inner(kind, &left, &right, args)
}

/// `assert_match!` 的内联函数
#[cold]
#[track_caller]
#[doc(hidden)]
pub fn assert_matches_failed<T: fmt::Debug + ?Sized>(
    left: &T,
    right: &str,
    args: Option<fmt::Arguments<'_>>,
) -> ! {
    // 使用 Display 实现来显示模式。
    struct Pattern<'a>(&'a str);
    impl fmt::Debug for Pattern<'_> {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            fmt::Display::fmt(self.0, f)
        }
    }
    assert_failed_inner(AssertKind::Match, &left, &Pattern(right), args);
}

/// 上述函数的非泛型版本,以避免代码膨胀。
#[track_caller]
fn assert_failed_inner(
    kind: AssertKind,
    left: &dyn fmt::Debug,
    right: &dyn fmt::Debug,
    args: Option<fmt::Arguments<'_>>,
) -> ! {
    let op = match kind {
        AssertKind::Eq => "==",
        AssertKind::Ne => "!=",
        AssertKind::Match => "matches",
    };

    match args {
        Some(args) => panic!(
            r#"assertion failed: `(left {} right)`
  left: `{:?}`,
 right: `{:?}`: {}"#,
            op, left, right, args
        ),
        None => panic!(
            r#"assertion failed: `(left {} right)`
  left: `{:?}`,
 right: `{:?}`"#,
            op, left, right,
        ),
    }
}