From 4c05757f1c54dc9d7c241dbacd6a0be3fa79c035 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 28 Sep 2023 21:32:34 +0000 Subject: [PATCH] Split out bad lit_to_const_for_patterns --- compiler/rustc_middle/src/query/erase.rs | 4 ++ compiler/rustc_middle/src/query/mod.rs | 12 +++- compiler/rustc_middle/src/ty/consts.rs | 15 ++--- compiler/rustc_mir_build/src/lib.rs | 1 + compiler/rustc_mir_build/src/thir/constant.rs | 60 +++++++++++++++++++ .../rustc_mir_build/src/thir/pattern/mod.rs | 4 +- compiler/rustc_ty_utils/src/consts.rs | 6 +- .../const-generics/projection-as-arg-const.rs | 8 +-- .../projection-as-arg-const.stderr | 14 ++--- 9 files changed, 97 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 8ba3764bcc317..86e7451da7761 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -182,6 +182,10 @@ impl EraseType for ty::Binder<'_, &'_ ty::List>> { type Result = [u8; size_of::>>>()]; } +impl EraseType for Option> { + type Result = [u8; size_of::>>()]; +} + impl EraseType for (&'_ T0, &'_ T1) { type Result = [u8; size_of::<(&'static (), &'static ())>()]; } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 340c5a769dbc4..235f20d96f59b 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1111,13 +1111,21 @@ rustc_queries! { desc { "getting a &core::panic::Location referring to a span" } } - // FIXME get rid of this with valtrees query lit_to_const( key: LitToConstInput<'tcx> - ) -> Result, LitToConstError> { + ) -> Option> { desc { "converting literal to const" } } + // FIXME: We could get rid of this if we got rid of float patterns, for + // example, but we would also need to make sure that other things like + // const type mismatches are handled elsewhere. + query lit_to_const_for_patterns( + key: LitToConstInput<'tcx> + ) -> Result, LitToConstError> { + desc { "converting literal to pattern const" } + } + query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } cache_on_disk_if { true } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 2518f0cf2e9ce..25fc3e726bc9d 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -194,17 +194,10 @@ impl<'tcx> Const<'tcx> { }; if let Some(lit_input) = lit_input { - // If an error occurred, ignore that it's a literal and leave reporting the error up to - // mir. - match tcx.at(expr.span).lit_to_const(lit_input) { - Ok(c) => return Some(c), - Err(e) => { - tcx.sess.delay_span_bug( - expr.span, - format!("Const::from_anon_const: couldn't lit_to_const {e:?}"), - ); - } - } + // If we failed to validate the type of the lit (which we sometimes + // need to actually build the valtree), then return `None`, which + // will make it fall back to using an unevaluated const. + return tcx.at(expr.span).lit_to_const(lit_input); } match expr.kind { diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 099fefbf06871..5220822025e48 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -32,6 +32,7 @@ fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { providers.check_match = thir::pattern::check_match; providers.lit_to_const = thir::constant::lit_to_const; + providers.lit_to_const_for_patterns = thir::constant::lit_to_const_for_patterns; providers.mir_built = build::mir_built; providers.closure_saved_names_of_captured_variables = build::closure_saved_names_of_captured_variables; diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index fbb74650faaeb..75ef51e939a8c 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -8,6 +8,66 @@ use crate::build::parse_float_into_scalar; pub(crate) fn lit_to_const<'tcx>( tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>, +) -> Option> { + let LitToConstInput { lit, ty, neg } = lit_input; + + let valtree = match (lit, &ty.kind()) { + (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => { + let str_bytes = s.as_str().as_bytes(); + ty::ValTree::from_raw_bytes(tcx, str_bytes) + } + (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) + if matches!(inner_ty.kind(), ty::Slice(_)) => + { + let bytes = data as &[u8]; + ty::ValTree::from_raw_bytes(tcx, bytes) + } + (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { + let bytes = data as &[u8]; + ty::ValTree::from_raw_bytes(tcx, bytes) + } + (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { + ty::ValTree::from_scalar_int((*n).into()) + } + (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().c_str()) => + { + let bytes = data as &[u8]; + ty::ValTree::from_raw_bytes(tcx, bytes) + } + (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { + let n = if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n }; + let param_ty = ParamEnv::reveal_all().and(ty); + let width = tcx + .layout_of(param_ty) + .unwrap_or_else(|_| bug!("should always be able to compute the layout of a scalar")) + .size; + trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); + let result = width.truncate(n); + trace!("trunc result: {}", result); + + let scalar_int = ScalarInt::try_from_uint(result, width) + .unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result)); + + ty::ValTree::from_scalar_int(scalar_int) + } + (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()), + (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()), + // If there's a type mismatch, it may be a legitimate type mismatch, or + // it might be an unnormalized type. Return `None`, which falls back + // to an unevaluated const, and the error will be caught (or we will see + // that it's ok) elsewhere. + _ => return None, + }; + + Some(ty::Const::new_value(tcx, valtree, ty)) +} + +/// The old "lit_to_const", which assumes that the type passed in `lit_input` +/// is normalized, and will error out if that is not true. This should only +/// be used in pattern building code (and ideally we'd get rid of this altogether). +pub(crate) fn lit_to_const_for_patterns<'tcx>( + tcx: TyCtxt<'tcx>, + lit_input: LitToConstInput<'tcx>, ) -> Result, LitToConstError> { let LitToConstInput { lit, ty, neg } = lit_input; diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index fe47a1cd78c49..92cb8d846f111 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -607,7 +607,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { _ => None, }; if let Some(lit_input) = lit_input { - match tcx.at(expr.span).lit_to_const(lit_input) { + match tcx.at(expr.span).lit_to_const_for_patterns(lit_input) { Ok(c) => return self.const_to_pat(Const::Ty(c), id, span, None).kind, // If an error occurred, ignore that it's a literal // and leave reporting the error up to const eval of @@ -676,7 +676,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let lit_input = LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg }; - match self.tcx.at(expr.span).lit_to_const(lit_input) { + match self.tcx.at(expr.span).lit_to_const_for_patterns(lit_input) { Ok(constant) => { self.const_to_pat(Const::Ty(constant), expr.hir_id, lit.span, None).kind } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 383cc996b9e69..49fc8b4dd5080 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -117,7 +117,11 @@ fn recurse_build<'tcx>( } &ExprKind::Literal { lit, neg } => { let sp = node.span; - match tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) { + match tcx.at(sp).lit_to_const_for_patterns(LitToConstInput { + lit: &lit.node, + ty: node.ty, + neg, + }) { Ok(c) => c, Err(LitToConstError::Reported(guar)) => ty::Const::new_error(tcx, guar, node.ty), Err(LitToConstError::TypeError) => { diff --git a/tests/ui/const-generics/projection-as-arg-const.rs b/tests/ui/const-generics/projection-as-arg-const.rs index 903548c75db56..6ba8612f31770 100644 --- a/tests/ui/const-generics/projection-as-arg-const.rs +++ b/tests/ui/const-generics/projection-as-arg-const.rs @@ -1,6 +1,7 @@ -// This is currently not possible to use projections as const generics. -// More information about this available here: -// https://github.com/rust-lang/rust/pull/104443#discussion_r1029375633 +// build-pass + +#![feature(adt_const_params)] +//~^ WARN the feature `adt_const_params` is incomplete pub trait Identity { type Identity; @@ -11,7 +12,6 @@ impl Identity for T { } pub fn foo::Identity>() { -//~^ ERROR assert!(X == 12); } diff --git a/tests/ui/const-generics/projection-as-arg-const.stderr b/tests/ui/const-generics/projection-as-arg-const.stderr index 803ed9c959752..6008e5cda163d 100644 --- a/tests/ui/const-generics/projection-as-arg-const.stderr +++ b/tests/ui/const-generics/projection-as-arg-const.stderr @@ -1,11 +1,11 @@ -error: `::Identity` is forbidden as the type of a const generic parameter - --> $DIR/projection-as-arg-const.rs:13:21 +warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/projection-as-arg-const.rs:3:12 | -LL | pub fn foo::Identity>() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![feature(adt_const_params)] + | ^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#![feature(adt_const_params)]` + = note: see issue #95174 for more information + = note: `#[warn(incomplete_features)]` on by default -error: aborting due to previous error +warning: 1 warning emitted