Skip to content

Commit

Permalink
Put normalization outside of lit_to_const query
Browse files Browse the repository at this point in the history
  • Loading branch information
GuillaumeGomez committed Nov 17, 2022
1 parent 9587fd7 commit 00d9f41
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 171 deletions.
234 changes: 115 additions & 119 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) {

if let Some(generics) = node.generics() {
for param in generics.params {
check_param_wf(tcx, param)
check_param_wf(tcx, param, def_id.def_id)
}
}
}
Expand Down Expand Up @@ -846,140 +846,136 @@ fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) {
check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig);
}

fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>, owner_id: LocalDefId) {
match param.kind {
// We currently only check wf of const params here.
hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => (),

// Const parameters are well formed if their type is structural match.
hir::GenericParamKind::Const { ty: hir_ty, default: _ } => {
let mut ty = tcx.type_of(param.def_id);
while let ty::Projection(ty::ProjectionTy { substs, item_def_id }) = ty.kind() {
let binder_ty = tcx.bound_type_of(*item_def_id);
ty = binder_ty.subst(tcx, substs);
}
enter_wf_checking_ctxt(tcx, param.span, owner_id, |wfcx| {
let ty = tcx.type_of(param.def_id);
let ty = wfcx.normalize(hir_ty.span, None, ty);

if tcx.features().adt_const_params {
if let Some(non_structural_match_ty) =
traits::search_for_adt_const_param_violation(param.span, tcx, ty)
{
// We use the same error code in both branches, because this is really the same
// issue: we just special-case the message for type parameters to make it
// clearer.
match non_structural_match_ty.kind() {
ty::Param(_) => {
// Const parameters may not have type parameters as their types,
// because we cannot be sure that the type parameter derives `PartialEq`
// and `Eq` (just implementing them is not enough for `structural_match`).
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"`{ty}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \
used as the type of a const parameter",
)
.span_label(
hir_ty.span,
format!("`{ty}` may not derive both `PartialEq` and `Eq`"),
)
.note(
"it is not currently possible to use a type parameter as the type of a \
const parameter",
)
.emit();
}
ty::Float(_) => {
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"`{ty}` is forbidden as the type of a const generic parameter",
)
.note("floats do not derive `Eq` or `Ord`, which are required for const parameters")
.emit();
}
ty::FnPtr(_) => {
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"using function pointers as const generic parameters is forbidden",
)
.emit();
}
ty::RawPtr(_) => {
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"using raw pointers as const generic parameters is forbidden",
)
.emit();
}
_ => {
let mut diag = struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
the type of a const parameter",
non_structural_match_ty,
);

if tcx.features().adt_const_params {
if let Some(non_structural_match_ty) =
traits::search_for_adt_const_param_violation(param.span, tcx, ty)
{
// We use the same error code in both branches, because this is really the same
// issue: we just special-case the message for type parameters to make it
// clearer.
match non_structural_match_ty.kind() {
ty::Param(_) => {
// Const parameters may not have type parameters as their types,
// because we cannot be sure that the type parameter derives `PartialEq`
// and `Eq` (just implementing them is not enough for `structural_match`).
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"`{ty}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \
used as the type of a const parameter",
)
.span_label(
hir_ty.span,
format!("`{ty}` may not derive both `PartialEq` and `Eq`"),
)
.note(
"it is not currently possible to use a type parameter as the type of a \
const parameter",
)
.emit();
}
ty::Float(_) => {
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"`{ty}` is forbidden as the type of a const generic parameter",
)
.note("floats do not derive `Eq` or `Ord`, which are required for const parameters")
.emit();
if ty == non_structural_match_ty {
diag.span_label(
hir_ty.span,
format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
);
}

diag.emit();
}
}
ty::FnPtr(_) => {
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"using function pointers as const generic parameters is forbidden",
)
.emit();
}
} else {
let err_ty_str;
let mut is_ptr = true;

let err = match ty.kind() {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None,
ty::FnPtr(_) => Some("function pointers"),
ty::RawPtr(_) => Some("raw pointers"),
_ => {
is_ptr = false;
err_ty_str = format!("`{ty}`");
Some(err_ty_str.as_str())
}
ty::RawPtr(_) => {
struct_span_err!(
tcx.sess,
};

if let Some(unsupported_type) = err {
if is_ptr {
tcx.sess.span_err(
hir_ty.span,
E0741,
"using raw pointers as const generic parameters is forbidden",
)
.emit();
}
// Should have been normalized in
// `traits::search_for_adt_const_param_violation`
ty::Projection(_) => unreachable!(),
_ => {
let mut diag = struct_span_err!(
tcx.sess,
&format!(
"using {unsupported_type} as const generic parameters is forbidden",
),
);
} else {
let mut err = tcx.sess.struct_span_err(
hir_ty.span,
E0741,
"`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
the type of a const parameter",
non_structural_match_ty,
&format!(
"{unsupported_type} is forbidden as the type of a const generic parameter",
),
);
err.note("the only supported types are integers, `bool` and `char`");
if tcx.sess.is_nightly_build() {
err.help(
"more complex types are supported with `#![feature(adt_const_params)]`",
);

if ty == non_structural_match_ty {
diag.span_label(
hir_ty.span,
format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
);
}

diag.emit();
err.emit();
}
}
}
} else {
let err_ty_str;
let mut is_ptr = true;

let err = match ty.kind() {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None,
ty::FnPtr(_) => Some("function pointers"),
ty::RawPtr(_) => Some("raw pointers"),
_ => {
is_ptr = false;
err_ty_str = format!("`{ty}`");
Some(err_ty_str.as_str())
}
};

if let Some(unsupported_type) = err {
if is_ptr {
tcx.sess.span_err(
hir_ty.span,
&format!(
"using {unsupported_type} as const generic parameters is forbidden",
),
);
} else {
let mut err = tcx.sess.struct_span_err(
hir_ty.span,
&format!(
"{unsupported_type} is forbidden as the type of a const generic parameter",
),
);
err.note("the only supported types are integers, `bool` and `char`");
if tcx.sess.is_nightly_build() {
err.help(
"more complex types are supported with `#![feature(adt_const_params)]`",
);
}
err.emit();
}
}
}
});
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/ty/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ impl<'tcx> Const<'tcx> {

let ty = tcx.type_of(def.def_id_for_type_of());

let param_env = tcx.param_env(def.did);
// We check that the `ty` is well formed in `wfcheck::check_param_wf` so
// this should always succeed.
let ty = tcx.normalize_erasing_regions(param_env, ty);

match Self::try_eval_lit_or_param(tcx, ty, expr) {
Some(v) => v,
None => tcx.mk_const(
Expand Down
66 changes: 23 additions & 43 deletions compiler/rustc_mir_build/src/thir/constant.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
use rustc_ast as ast;
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
use rustc_middle::ty::{self, ParamEnv, ScalarInt, Ty, TyCtxt};
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{self, ParamEnv, ScalarInt, TyCtxt};
use rustc_span::DUMMY_SP;

fn trunc<'tcx>(
pub(crate) fn lit_to_const<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
lit: &ast::LitKind,
n: u128,
) -> Result<ScalarInt, LitToConstError> {
let param_ty = ParamEnv::reveal_all().and(ty);
let width =
tcx.layout_of(param_ty)
lit_input: LitToConstInput<'tcx>,
) -> Result<ty::Const<'tcx>, LitToConstError> {
let LitToConstInput { lit, ty, neg } = lit_input;
assert!(!TypeVisitable::has_projections(&ty));

let trunc = |n| {
let param_ty = ParamEnv::reveal_all().and(ty);
let width = tcx
.layout_of(param_ty)
.map_err(|_| {
LitToConstError::Reported(tcx.sess.delay_span_bug(
DUMMY_SP,
format!("couldn't compute width of literal: {:?}", lit),
format!("couldn't compute width of literal: {:?}", lit_input.lit),
))
})?
.size;
trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
let result = width.truncate(n);
trace!("trunc result: {}", result);
trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
let result = width.truncate(n);
trace!("trunc result: {}", result);

Ok(ScalarInt::try_from_uint(result, width)
.unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result)))
}
Ok(ScalarInt::try_from_uint(result, width)
.unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result)))
};

fn get_valtree<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
neg: bool,
lit: &ast::LitKind,
) -> Result<ty::ValTree<'tcx>, LitToConstError> {
Ok(match (lit, &ty.kind()) {
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)
Expand All @@ -52,12 +49,8 @@ fn get_valtree<'tcx>(
ty::ValTree::from_scalar_int((*n).into())
}
(ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => {
let scalar_int = trunc(
tcx,
ty,
lit,
if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n },
)?;
let scalar_int =
trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })?;
ty::ValTree::from_scalar_int(scalar_int)
}
(ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()),
Expand All @@ -67,21 +60,8 @@ fn get_valtree<'tcx>(
tcx.sess.delay_span_bug(DUMMY_SP, "encountered LitKind::Err during mir build"),
));
}
(_, ty::Projection(ty::ProjectionTy { substs, item_def_id })) => {
let binder_ty = tcx.bound_type_of(*item_def_id);
let ty = binder_ty.subst(tcx, substs);
get_valtree(tcx, ty, neg, lit)?
}
_ => return Err(LitToConstError::TypeError),
})
}

pub(crate) fn lit_to_const<'tcx>(
tcx: TyCtxt<'tcx>,
lit_input: LitToConstInput<'tcx>,
) -> Result<ty::Const<'tcx>, LitToConstError> {
let LitToConstInput { lit, ty, neg } = lit_input;
};

let valtree = get_valtree(tcx, ty, neg, lit)?;
Ok(ty::Const::from_value(tcx, valtree, ty))
}
Loading

0 comments on commit 00d9f41

Please sign in to comment.