| use rustc_abi::Size; | |
| use rustc_ast::{self as ast, UintTy}; | |
| use rustc_hir::LangItem; | |
| use rustc_middle::bug; | |
| use rustc_middle::ty::{self, LitToConstInput, ScalarInt, Ty, TyCtxt, TypeVisitableExt as _}; | |
| use tracing::trace; | |
| use crate::builder::parse_float_into_scalar; | |
| pub(crate) fn lit_to_const<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| lit_input: LitToConstInput<'tcx>, | |
| ) -> Option<ty::Value<'tcx>> { | |
| let LitToConstInput { lit, ty: expected_ty, neg } = lit_input; | |
| if expected_ty.error_reported().is_err() { | |
| return None; | |
| } | |
| let trunc = |n, width: ty::UintTy| { | |
| let width = width | |
| .normalize(tcx.data_layout.pointer_size().bits().try_into().unwrap()) | |
| .bit_width() | |
| .unwrap(); | |
| let width = Size::from_bits(width); | |
| trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); | |
| let result = width.truncate(n); | |
| trace!("trunc result: {}", result); | |
| ScalarInt::try_from_uint(result, width) | |
| .unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result)) | |
| }; | |
| let (valtree, valtree_ty) = match (lit, expected_ty.map(|ty| ty.kind())) { | |
| (ast::LitKind::Str(s, _), _) => { | |
| let str_bytes = s.as_str().as_bytes(); | |
| let valtree_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_); | |
| (ty::ValTree::from_raw_bytes(tcx, str_bytes), valtree_ty) | |
| } | |
| (ast::LitKind::ByteStr(byte_sym, _), Some(ty::Ref(_, inner_ty, _))) | |
| if let ty::Slice(ty) | ty::Array(ty, _) = inner_ty.kind() | |
| && let ty::Uint(UintTy::U8) = ty.kind() => | |
| { | |
| (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty.unwrap()) | |
| } | |
| ( | |
| ast::LitKind::ByteStr(byte_sym, _), | |
| Some(ty::Slice(inner_ty) | ty::Array(inner_ty, _)), | |
| ) if tcx.features().deref_patterns() | |
| && let ty::Uint(UintTy::U8) = inner_ty.kind() => | |
| { | |
| // Byte string literal patterns may have type `[u8]` or `[u8; N]` if `deref_patterns` is | |
| // enabled, in order to allow, e.g., `deref!(b"..."): Vec<u8>`. | |
| (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty.unwrap()) | |
| } | |
| (ast::LitKind::ByteStr(byte_sym, _), _) => { | |
| let valtree = ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()); | |
| let valtree_ty = Ty::new_array(tcx, tcx.types.u8, byte_sym.as_byte_str().len() as u64); | |
| (valtree, valtree_ty) | |
| } | |
| (ast::LitKind::Byte(n), _) => (ty::ValTree::from_scalar_int(tcx, n.into()), tcx.types.u8), | |
| (ast::LitKind::CStr(byte_sym, _), _) | |
| if let Some(cstr_def_id) = tcx.lang_items().get(LangItem::CStr) => | |
| { | |
| // A CStr is a newtype around a byte slice, so we create the inner slice here. | |
| // We need a branch for each "level" of the data structure. | |
| let cstr_ty = tcx.type_of(cstr_def_id).skip_binder(); | |
| let bytes = ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()); | |
| let valtree = | |
| ty::ValTree::from_branches(tcx, [ty::Const::new_value(tcx, bytes, cstr_ty)]); | |
| let valtree_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, cstr_ty); | |
| (valtree, valtree_ty) | |
| } | |
| (ast::LitKind::Int(n, ast::LitIntType::Unsigned(ui)), _) if !neg => { | |
| let scalar_int = trunc(n.get(), ui); | |
| (ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_uint(tcx, ui)) | |
| } | |
| (ast::LitKind::Int(_, ast::LitIntType::Unsigned(_)), _) if neg => return None, | |
| (ast::LitKind::Int(n, ast::LitIntType::Signed(i)), _) => { | |
| let scalar_int = | |
| trunc(if neg { u128::wrapping_neg(n.get()) } else { n.get() }, i.to_unsigned()); | |
| (ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_int(tcx, i)) | |
| } | |
| (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), Some(ty::Uint(ui))) if !neg => { | |
| let scalar_int = trunc(n.get(), *ui); | |
| (ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_uint(tcx, *ui)) | |
| } | |
| (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), Some(ty::Int(i))) => { | |
| // Unsigned "negation" has the same bitwise effect as signed negation, | |
| // which gets the result we want without additional casts. | |
| let scalar_int = | |
| trunc(if neg { u128::wrapping_neg(n.get()) } else { n.get() }, i.to_unsigned()); | |
| (ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_int(tcx, *i)) | |
| } | |
| (ast::LitKind::Bool(b), _) => (ty::ValTree::from_scalar_int(tcx, b.into()), tcx.types.bool), | |
| (ast::LitKind::Float(n, ast::LitFloatType::Suffixed(fty)), _) => { | |
| let fty = match fty { | |
| ast::FloatTy::F16 => ty::FloatTy::F16, | |
| ast::FloatTy::F32 => ty::FloatTy::F32, | |
| ast::FloatTy::F64 => ty::FloatTy::F64, | |
| ast::FloatTy::F128 => ty::FloatTy::F128, | |
| }; | |
| let bits = parse_float_into_scalar(n, fty, neg)?; | |
| (ty::ValTree::from_scalar_int(tcx, bits), Ty::new_float(tcx, fty)) | |
| } | |
| (ast::LitKind::Float(n, ast::LitFloatType::Unsuffixed), Some(ty::Float(fty))) => { | |
| let bits = parse_float_into_scalar(n, *fty, neg)?; | |
| (ty::ValTree::from_scalar_int(tcx, bits), Ty::new_float(tcx, *fty)) | |
| } | |
| (ast::LitKind::Char(c), _) => (ty::ValTree::from_scalar_int(tcx, c.into()), tcx.types.char), | |
| (ast::LitKind::Err(_), _) => return None, | |
| _ => return None, | |
| }; | |
| Some(ty::Value { ty: valtree_ty, valtree }) | |
| } |