use std::{collections::HashMap, fmt::Display};
use rustc_span::Span;
use vir::ast::VirErr;
pub(crate) fn err_span>(span: Span, msg: S) -> Result {
Err(vir::messages::error(&crate::spans::err_air_span(span), msg))
}
pub(crate) fn err_span_vec>(span: Span, msg: S) -> Result> {
Err(vec![vir::messages::error(&crate::spans::err_air_span(span), msg)])
}
pub(crate) fn err_span_bare>(span: Span, msg: S) -> VirErr {
vir::messages::error(&crate::spans::err_air_span(span), msg)
}
pub(crate) fn vir_err_span_str(span: Span, msg: &str) -> VirErr {
vir_err_span_string(span, msg.to_string())
}
pub(crate) fn vir_err_span_string(span: Span, msg: String) -> VirErr {
vir::messages::error(&crate::spans::err_air_span(span), msg)
}
pub(crate) fn unsupported_err_span(span: Span, msg: String) -> Result {
err_span(span, format!("Verus does not currently support {}", msg))
}
pub(crate) fn internal_err_span(span: Span, msg: String) -> Result {
vir::util::internal_err_span(crate::spans::err_air_span(span), msg)
}
#[macro_export]
macro_rules! unsupported_err {
($span: expr, $msg: expr) => {{
$crate::util::unsupported_err_span($span, $msg.to_string())?;
unreachable!()
}};
($span: expr, $msg: expr, $info: expr) => {{
dbg!($info);
$crate::util::unsupported_err_span($span, $msg.to_string())?;
unreachable!()
}};
}
#[macro_export]
macro_rules! unsupported_err_vec {
($span: expr, $msg: expr) => {{
$crate::util::unsupported_err_span($span, $msg.to_string()).map_err(|e| vec![e])?;
unreachable!()
}};
($span: expr, $msg: expr, $info: expr) => {{
dbg!($info);
$crate::util::unsupported_err_span($span, $msg.to_string()).map_err(|e| vec![e])?;
unreachable!()
}};
}
#[macro_export]
macro_rules! internal_err {
($span: expr, $msg: expr) => {{
$crate::util::internal_err_span($span, $msg.to_string())?;
unreachable!()
}};
($span: expr, $msg: expr, $info: expr) => {{
dbg!($info);
$crate::util::internal_err_span($span, $msg.to_string())?;
unreachable!()
}};
}
#[macro_export]
macro_rules! internal_err_unless {
($assertion: expr, $span: expr, $msg: expr) => {
if (!$assertion) {
$crate::util::internal_err_span($span, $msg.to_string())?;
}
};
($assertion: expr, $span: expr, $msg: expr, $info: expr) => {
if (!$assertion) {
dbg!($info);
$crate::util::internal_err_span($span, $msg.to_string())?;
}
};
}
#[macro_export]
macro_rules! unsupported_err_unless {
($assertion: expr, $span: expr, $msg: expr) => {
if (!$assertion) {
$crate::util::unsupported_err_span($span, $msg.to_string())?;
}
};
($assertion: expr, $span: expr, $msg: expr, $info: expr) => {
if (!$assertion) {
dbg!($info);
$crate::util::unsupported_err_span($span, $msg.to_string())?;
}
};
}
#[macro_export]
macro_rules! unsupported_err_vec_unless {
($assertion: expr, $span: expr, $msg: expr) => {
if (!$assertion) {
$crate::util::unsupported_err_span($span, $msg.to_string()).map_err(|e| vec![e])?;
}
};
($assertion: expr, $span: expr, $msg: expr, $info: expr) => {
if (!$assertion) {
dbg!($info);
$crate::util::unsupported_err_span($span, $msg.to_string()).map_err(|e| vec![e])?;
}
};
}
#[macro_export]
macro_rules! err_unless {
($assertion: expr, $span: expr, $msg: expr) => {
if (!$assertion) {
$crate::util::err_span($span, $msg)?;
}
};
($assertion: expr, $span: expr, $msg: expr, $info: expr) => {
if (!$assertion) {
dbg!($info);
$crate::util::err_span($span, $msg)?;
}
};
}
/// Basic error, with just a message
pub fn error>(msg: S) -> VirErr {
vir::messages::error_bare(msg)
}
#[allow(dead_code)]
pub(crate) fn vec_map B>(v: &Vec, f: F) -> Vec {
v.iter().map(f).collect()
}
#[allow(dead_code)]
pub(crate) fn vec_map_result(v: &Vec, f: F) -> Result, E>
where
F: Fn(&A) -> Result,
{
v.iter().map(f).collect()
}
#[allow(dead_code)]
pub(crate) fn slice_vec_map_result Result>(
slice: &[A],
f: F,
) -> Result, E> {
slice.iter().map(f).collect()
}
pub enum VerusBuildProfile {
Debug,
Release,
Unknown,
}
impl std::fmt::Display for VerusBuildProfile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
VerusBuildProfile::Debug => "debug",
VerusBuildProfile::Release => "release",
VerusBuildProfile::Unknown => "unknown",
};
f.write_str(s)
}
}
const fn verus_build_profile() -> VerusBuildProfile {
let profile = option_env!("VARGO_BUILD_PROFILE");
match profile {
Some(p) => {
if const_str_equal(p, "release") {
VerusBuildProfile::Release
} else if const_str_equal(p, "debug") {
VerusBuildProfile::Debug
} else {
panic!("unexpected VARGO_BUILD_PROFILE");
}
}
None => VerusBuildProfile::Unknown,
}
}
pub struct VerusBuildInfo {
pub profile: VerusBuildProfile,
pub version: &'static str,
pub platform_os: &'static str,
pub platform_arch: &'static str,
pub toolchain: &'static str,
pub sha: &'static str,
}
const VARGO_TOOLCHAIN: Option<&'static str> = option_env!("VARGO_TOOLCHAIN");
pub const fn verus_build_info() -> VerusBuildInfo {
let profile = verus_build_profile();
let version = match option_env!("VARGO_BUILD_VERSION") {
Some(v) => v,
None => "Unknown",
};
let platform_os = std::env::consts::OS;
let platform_arch = std::env::consts::ARCH;
let toolchain = match VARGO_TOOLCHAIN {
Some(toolchain) => toolchain,
None => "unkown",
};
let sha = match option_env!("VARGO_BUILD_SHA") {
Some(v) => v,
None => "Unknown",
};
VerusBuildInfo { profile, version, platform_os, platform_arch, toolchain, sha }
}
impl Display for VerusBuildInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Verus")?;
writeln!(f, " Version: {}", self.version)?;
writeln!(f, " Profile: {}", self.profile)?;
writeln!(f, " Platform: {}_{}", self.platform_os, self.platform_arch)?;
writeln!(f, " Toolchain: {}", self.toolchain)?;
Ok(())
}
}
impl VerusBuildInfo {
pub fn to_json(&self) -> serde_json::Value {
serde_json::json!({
"verus": {
"profile": self.profile.to_string(),
"version": self.version.to_string(),
"platform": {
"os": self.platform_os.to_string(),
"arch": self.platform_arch.to_string(),
},
"toolchain": self.toolchain.to_string(),
"commit": self.sha,
}
})
}
}
// ==================================================================================================
// this function was copied from https://github.com/Nugine/const-str/blob/main/const-str/src/bytes.rs
// const-str is MIT licensed
pub const fn const_str_equal(lhs: &str, rhs: &str) -> bool {
let lhs = lhs.as_bytes();
let rhs = rhs.as_bytes();
if lhs.len() != rhs.len() {
return false;
}
let mut i = 0;
while i < lhs.len() {
if lhs[i] != rhs[i] {
return false;
}
i += 1;
}
true
}
// ==================================================================================================
// this is unfortunate, but when processing a reveal we cannot get the type of the
// corresponding node in HIR because the body of the reveal closure cannot be
// successfully typechecked, and I did not find a public interface to this in rustc
// NOTE: do not use this if you have a body context with `types` or can otherwise obtain TypeckResults
pub fn hir_prim_ty_to_mir_ty<'tcx>(
tcx: rustc_middle::ty::TyCtxt<'tcx>,
prim_ty: &rustc_hir::PrimTy,
) -> rustc_middle::ty::Ty<'tcx> {
match prim_ty {
rustc_hir::PrimTy::Int(int_ty) => match int_ty {
rustc_ast::IntTy::Isize => tcx.types.isize,
rustc_ast::IntTy::I8 => tcx.types.i8,
rustc_ast::IntTy::I16 => tcx.types.i16,
rustc_ast::IntTy::I32 => tcx.types.i32,
rustc_ast::IntTy::I64 => tcx.types.i64,
rustc_ast::IntTy::I128 => tcx.types.i128,
},
rustc_hir::PrimTy::Uint(uint_ty) => match uint_ty {
rustc_ast::UintTy::Usize => tcx.types.usize,
rustc_ast::UintTy::U8 => tcx.types.u8,
rustc_ast::UintTy::U16 => tcx.types.u16,
rustc_ast::UintTy::U32 => tcx.types.u32,
rustc_ast::UintTy::U64 => tcx.types.u64,
rustc_ast::UintTy::U128 => tcx.types.u128,
},
rustc_hir::PrimTy::Float(float_ty) => match float_ty {
rustc_ast::FloatTy::F16 => tcx.types.f16,
rustc_ast::FloatTy::F32 => tcx.types.f32,
rustc_ast::FloatTy::F64 => tcx.types.f64,
rustc_ast::FloatTy::F128 => tcx.types.f128,
},
rustc_hir::PrimTy::Str => tcx.types.str_,
rustc_hir::PrimTy::Bool => tcx.types.bool,
rustc_hir::PrimTy::Char => tcx.types.char,
}
}
#[allow(unused_macros)]
macro_rules! dbg_backtrace {
($val: expr) => {
match $val {
tmp => {
std::eprintln!(
"[{}:{}:{}] {} = {:#?}",
std::file!(),
std::line!(),
std::column!(),
std::stringify!($val),
&tmp
);
eprintln!(
"[{}:{}:{}]\n{}",
std::file!(),
std::line!(),
std::column!(),
std::backtrace::Backtrace::force_capture()
);
tmp
}
}
};
}
#[allow(unused_imports)]
pub(crate) use dbg_backtrace;
#[allow(unused_macros)]
macro_rules! backtrace {
() => {
eprintln!(
"[{}:{}:{}]\n{}",
std::file!(),
std::line!(),
std::column!(),
std::backtrace::Backtrace::force_capture()
);
};
}
#[allow(unused_imports)]
pub(crate) use backtrace;
pub trait HashMapAbsorbWith {
fn absorb_with(&mut self, other: HashMap, absorb_value: F)
where
F: Fn(&mut V, V);
}
impl HashMapAbsorbWith for HashMap
where
K: Eq + std::hash::Hash,
{
fn absorb_with(&mut self, other: HashMap, absorb_value: F)
where
F: Fn(&mut V, V),
{
use std::collections::hash_map::Entry;
for (key, rhs) in other {
match self.entry(key) {
Entry::Vacant(vacant_entry) => {
vacant_entry.insert(rhs);
}
Entry::Occupied(mut occupied_entry) => {
absorb_value(occupied_entry.get_mut(), rhs);
}
}
}
}
}
pub(crate) fn no_builtin_err(span: &vir::messages::Span) -> VirErr {
vir::messages::error(span,
"the `verus_builtin` crate was not imported but it is necessary to run Verus; you likely need to add `use vstd::prelude::*;` at the top of a lib.rs or main.rs file")
.help("for getting started with Verus, see: https://verus-lang.github.io/verus/guide/getting_started.html")
}
pub(crate) fn iter_crate_free_items<'tcx>(
tcx: rustc_middle::ty::TyCtxt<'tcx>,
) -> impl Iterator- > {
tcx.hir_crate_items(()).free_items().map(move |item_id| tcx.hir_item(item_id))
}