Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,10 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::DynCompatibilityViolation;
use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::mir::interpret::LitToConstInput;
use rustc_middle::ty::print::PrintPolyTraitRefExt as _;
use rustc_middle::ty::{
self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt,
TypeSuperFoldable, TypeVisitableExt, TypingMode, Upcast, fold_regions,
self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, LitToConstInput, Ty, TyCtxt,
TypeSuperFoldable, TypeVisitableExt, TypingMode, Upcast, const_lit_matches_ty, fold_regions,
};
use rustc_middle::{bug, span_bug};
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
Expand Down Expand Up @@ -2803,8 +2802,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
span: Span,
) -> Const<'tcx> {
let tcx = self.tcx();
if let LitKind::Err(guar) = *kind {
return ty::Const::new_error(tcx, guar);
}
let input = LitToConstInput { lit: *kind, ty, neg };
tcx.at(span).lit_to_const(input)
match tcx.at(span).lit_to_const(input) {
Some(value) => ty::Const::new_value(tcx, value.valtree, value.ty),
None => {
let e = tcx.dcx().span_err(span, "type annotations needed for the literal");
ty::Const::new_error(tcx, e)
}
}
}

#[instrument(skip(self), level = "debug")]
Expand Down Expand Up @@ -2833,11 +2841,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
_ => None,
};

lit_input
// Allow the `ty` to be an alias type, though we cannot handle it here, we just go through
// the more expensive anon const code path.
.filter(|l| !l.ty.has_aliases())
.map(|l| tcx.at(expr.span).lit_to_const(l))
lit_input.and_then(|l| {
if const_lit_matches_ty(tcx, &l.lit, l.ty, l.neg) {
tcx.at(expr.span)
.lit_to_const(l)
.map(|value| ty::Const::new_value(tcx, value.valtree, value.ty))
} else {
None
}
})
}

fn require_type_const_attribute(
Expand Down
13 changes: 1 addition & 12 deletions compiler/rustc_middle/src/mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::num::NonZero;
use std::{fmt, io};

use rustc_abi::{AddressSpace, Align, Endian, HasDataLayout, Size};
use rustc_ast::{LitKind, Mutability};
use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sharded::ShardedHashMap;
use rustc_data_structures::sync::{AtomicU64, Lock};
Expand Down Expand Up @@ -73,17 +73,6 @@ impl<'tcx> GlobalId<'tcx> {
}
}

/// Input argument for `tcx.lit_to_const`.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, HashStable)]
pub struct LitToConstInput<'tcx> {
/// The absolute value of the resultant constant.
pub lit: LitKind,
/// The type of the constant.
pub ty: Ty<'tcx>,
/// If the constant is negative.
pub neg: bool,
}

#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct AllocId(pub NonZero<u64>);

Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_middle/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ use crate::middle::resolve_bound_vars::{ObjectLifetimeDefault, ResolveBoundVars,
use crate::middle::stability::DeprecationEntry;
use crate::mir::interpret::{
EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult,
EvalToValTreeResult, GlobalId, LitToConstInput,
EvalToValTreeResult, GlobalId,
};
use crate::mir::mono::{
CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono,
Expand All @@ -135,8 +135,8 @@ use crate::ty::layout::ValidityRequirement;
use crate::ty::print::PrintTraitRefExt;
use crate::ty::util::AlwaysRequiresDrop;
use crate::ty::{
self, CrateInherentImpls, GenericArg, GenericArgsRef, PseudoCanonicalInput, SizedTraitKind, Ty,
TyCtxt, TyCtxtFeed,
self, CrateInherentImpls, GenericArg, GenericArgsRef, LitToConstInput, PseudoCanonicalInput,
SizedTraitKind, Ty, TyCtxt, TyCtxtFeed,
};
use crate::{dep_graph, mir, thir};

Expand Down Expand Up @@ -1412,7 +1412,7 @@ rustc_queries! {
// FIXME get rid of this with valtrees
query lit_to_const(
key: LitToConstInput<'tcx>
) -> ty::Const<'tcx> {
) -> Option<ty::Value<'tcx>> {
desc { "converting literal to const" }
}

Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_middle/src/query/erase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ impl Erasable for Option<ty::EarlyBinder<'_, Ty<'_>>> {
type Storage = [u8; size_of::<Option<ty::EarlyBinder<'static, Ty<'static>>>>()];
}

impl Erasable for Option<ty::Value<'_>> {
type Storage = [u8; size_of::<Option<ty::Value<'static>>>()];
}

impl Erasable for rustc_hir::MaybeOwner<'_> {
type Storage = [u8; size_of::<rustc_hir::MaybeOwner<'static>>()];
}
Expand Down Expand Up @@ -441,7 +445,6 @@ impl_erasable_for_single_lifetime_types! {
rustc_middle::mir::DestructuredConstant,
rustc_middle::mir::ConstAlloc,
rustc_middle::mir::interpret::GlobalId,
rustc_middle::mir::interpret::LitToConstInput,
rustc_middle::mir::interpret::EvalStaticInitializerRawResult,
rustc_middle::mir::mono::MonoItemPartitions,
rustc_middle::traits::query::MethodAutoderefStepsResult,
Expand All @@ -466,6 +469,7 @@ impl_erasable_for_single_lifetime_types! {
rustc_middle::ty::InstanceKind,
rustc_middle::ty::layout::FnAbiError,
rustc_middle::ty::layout::LayoutError,
rustc_middle::ty::LitToConstInput,
rustc_middle::ty::ParamEnv,
rustc_middle::ty::TypingEnv,
rustc_middle::ty::Predicate,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/query/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl<'tcx> Key for (Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>) {
}
}

impl<'tcx> Key for mir::interpret::LitToConstInput<'tcx> {
impl<'tcx> Key for ty::LitToConstInput<'tcx> {
fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
DUMMY_SP
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/ty/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ use crate::ty::{self, Ty, TyCtxt};

mod int;
mod kind;
mod lit;
mod valtree;

pub use int::*;
pub use kind::*;
pub use lit::*;
use rustc_span::{DUMMY_SP, ErrorGuaranteed};
pub use valtree::*;

Expand Down
55 changes: 55 additions & 0 deletions compiler/rustc_middle/src/ty/consts/lit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use rustc_ast::LitKind;
use rustc_hir;
use rustc_macros::HashStable;

use crate::ty::{self, Ty, TyCtxt};

/// Input argument for `tcx.lit_to_const`.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, HashStable)]
pub struct LitToConstInput<'tcx> {
/// The absolute value of the resultant constant.
pub lit: LitKind,
/// The type of the constant.
pub ty: Ty<'tcx>,
/// If the constant is negative.
pub neg: bool,
}

/// Checks whether a literal can be interpreted as a const of the given type.
pub fn const_lit_matches_ty<'tcx>(
tcx: TyCtxt<'tcx>,
kind: &LitKind,
ty: Ty<'tcx>,
neg: bool,
) -> bool {
match (*kind, ty.kind()) {
(LitKind::Str(..), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => true,
(LitKind::Str(..), ty::Str) if tcx.features().deref_patterns() => true,
(LitKind::ByteStr(..), ty::Ref(_, inner_ty, _))
if let ty::Slice(ty) | ty::Array(ty, _) = inner_ty.kind()
&& matches!(ty.kind(), ty::Uint(ty::UintTy::U8)) =>
{
true
}
(LitKind::ByteStr(..), ty::Slice(inner_ty) | ty::Array(inner_ty, _))
if tcx.features().deref_patterns()
&& matches!(inner_ty.kind(), ty::Uint(ty::UintTy::U8)) =>
{
true
}
(LitKind::Byte(..), ty::Uint(ty::UintTy::U8)) => true,
(LitKind::CStr(..), ty::Ref(_, inner_ty, _))
if matches!(inner_ty.kind(), ty::Adt(def, _)
if tcx.is_lang_item(def.did(), rustc_hir::LangItem::CStr)) =>
{
true
}
(LitKind::Int(..), ty::Uint(_)) if !neg => true,
(LitKind::Int(..), ty::Int(_)) => true,
(LitKind::Bool(..), ty::Bool) => true,
(LitKind::Float(..), ty::Float(_)) => true,
(LitKind::Char(..), ty::Char) => true,
(LitKind::Err(..), _) => true,
_ => false,
}
}
5 changes: 3 additions & 2 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ pub use self::closure::{
place_to_string_for_capture,
};
pub use self::consts::{
AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, Expr, ExprKind, ScalarInt,
SimdAlign, UnevaluatedConst, ValTree, ValTreeKindExt, Value,
AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, Expr, ExprKind,
LitToConstInput, ScalarInt, SimdAlign, UnevaluatedConst, ValTree, ValTreeKindExt, Value,
const_lit_matches_ty,
};
pub use self::context::{
CtxtInterners, CurrentGcx, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, tls,
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_mir_build/src/builder/expr/as_constant.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
//! See docs in build/expr/mod.rs
//! See docs in builder/expr/mod.rs

use rustc_abi::Size;
use rustc_ast::{self as ast};
use rustc_hir::LangItem;
use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, LitToConstInput, Scalar};
use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, Scalar};
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::{
self, CanonicalUserType, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt as _,
UserTypeAnnotationIndex,
self, CanonicalUserType, CanonicalUserTypeAnnotation, LitToConstInput, Ty, TyCtxt,
TypeVisitableExt as _, UserTypeAnnotationIndex,
};
use rustc_middle::{bug, mir, span_bug};
use tracing::{instrument, trace};
Expand Down
90 changes: 55 additions & 35 deletions compiler/rustc_mir_build/src/thir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@ use rustc_abi::Size;
use rustc_ast::{self as ast, UintTy};
use rustc_hir::LangItem;
use rustc_middle::bug;
use rustc_middle::mir::interpret::LitToConstInput;
use rustc_middle::ty::{self, ScalarInt, TyCtxt, TypeVisitableExt as _};
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>,
) -> ty::Const<'tcx> {
let LitToConstInput { lit, ty, neg } = lit_input;
) -> Option<ty::Value<'tcx>> {
let LitToConstInput { lit, ty: expected_ty, neg } = lit_input;

if let Err(guar) = ty.error_reported() {
return ty::Const::new_error(tcx, guar);
if expected_ty.error_reported().is_err() {
return None;
}

let trunc = |n, width: ty::UintTy| {
Expand All @@ -32,63 +31,84 @@ pub(crate) fn lit_to_const<'tcx>(
.unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result))
};

let valtree = match (lit, ty.kind()) {
(ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
let (valtree, valtree_ty) = match (lit, expected_ty.kind()) {
(ast::LitKind::Str(s, _), _) => {
let str_bytes = s.as_str().as_bytes();
ty::ValTree::from_raw_bytes(tcx, str_bytes)
}
(ast::LitKind::Str(s, _), ty::Str) if tcx.features().deref_patterns() => {
// String literal patterns may have type `str` if `deref_patterns` is enabled, in order
// to allow `deref!("..."): String`.
let str_bytes = s.as_str().as_bytes();
ty::ValTree::from_raw_bytes(tcx, str_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, _), 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())
(ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty)
}
(ast::LitKind::ByteStr(byte_sym, _), 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())
(ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty)
}
(ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
ty::ValTree::from_scalar_int(tcx, n.into())
(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::CStr(byte_sym, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) =>
(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());
ty::ValTree::from_branches(tcx, [ty::Const::new_value(tcx, bytes, *inner_ty)])
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, _), ty::Uint(ui)) if !neg => {
(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), ty::Uint(ui)) if !neg => {
let scalar_int = trunc(n.get(), *ui);
ty::ValTree::from_scalar_int(tcx, scalar_int)
(ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_uint(tcx, *ui))
}
(ast::LitKind::Int(n, _), ty::Int(i)) => {
(ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), 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::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::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int(tcx, b.into()),
(ast::LitKind::Float(n, _), ty::Float(fty)) => {
let bits = parse_float_into_scalar(n, *fty, neg).unwrap_or_else(|| {
tcx.dcx().bug(format!("couldn't parse float literal: {:?}", lit_input.lit))
});
ty::ValTree::from_scalar_int(tcx, bits)
(ast::LitKind::Float(n, ast::LitFloatType::Unsuffixed), 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::Char) => ty::ValTree::from_scalar_int(tcx, c.into()),
(ast::LitKind::Err(guar), _) => return ty::Const::new_error(tcx, guar),
_ => return ty::Const::new_misc_error(tcx),
(ast::LitKind::Char(c), _) => (ty::ValTree::from_scalar_int(tcx, c.into()), tcx.types.char),
(ast::LitKind::Err(_), _) => return None,
_ => return None,
};

ty::Const::new_value(tcx, valtree, ty)
Some(ty::Value { ty: valtree_ty, valtree })
}
Loading
Loading