Skip to content

Commit 291bc0d

Browse files
Add normalization for projection types in const generics
1 parent 7c75fe4 commit 291bc0d

File tree

2 files changed

+162
-145
lines changed

2 files changed

+162
-145
lines changed

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+127-118
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,132 @@ fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) {
846846
check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig);
847847
}
848848

849+
fn check_param_wf_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, hir_ty: &rustc_hir::Ty<'_>, param_span: Span) {
850+
if tcx.features().adt_const_params {
851+
if let Some(non_structural_match_ty) =
852+
traits::search_for_adt_const_param_violation(param_span, tcx, ty)
853+
{
854+
// We use the same error code in both branches, because this is really the same
855+
// issue: we just special-case the message for type parameters to make it
856+
// clearer.
857+
match non_structural_match_ty.kind() {
858+
ty::Param(_) => {
859+
// Const parameters may not have type parameters as their types,
860+
// because we cannot be sure that the type parameter derives `PartialEq`
861+
// and `Eq` (just implementing them is not enough for `structural_match`).
862+
struct_span_err!(
863+
tcx.sess,
864+
hir_ty.span,
865+
E0741,
866+
"`{ty}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \
867+
used as the type of a const parameter",
868+
)
869+
.span_label(
870+
hir_ty.span,
871+
format!("`{ty}` may not derive both `PartialEq` and `Eq`"),
872+
)
873+
.note(
874+
"it is not currently possible to use a type parameter as the type of a \
875+
const parameter",
876+
)
877+
.emit();
878+
}
879+
ty::Float(_) => {
880+
struct_span_err!(
881+
tcx.sess,
882+
hir_ty.span,
883+
E0741,
884+
"`{ty}` is forbidden as the type of a const generic parameter",
885+
)
886+
.note("floats do not derive `Eq` or `Ord`, which are required for const parameters")
887+
.emit();
888+
}
889+
ty::FnPtr(_) => {
890+
struct_span_err!(
891+
tcx.sess,
892+
hir_ty.span,
893+
E0741,
894+
"using function pointers as const generic parameters is forbidden",
895+
)
896+
.emit();
897+
}
898+
ty::RawPtr(_) => {
899+
struct_span_err!(
900+
tcx.sess,
901+
hir_ty.span,
902+
E0741,
903+
"using raw pointers as const generic parameters is forbidden",
904+
)
905+
.emit();
906+
}
907+
ty::Projection(ty::ProjectionTy { substs, item_def_id }) => {
908+
let binder_ty = tcx.bound_type_of(*item_def_id);
909+
let ty = binder_ty.subst(tcx, substs);
910+
check_param_wf_ty(tcx, ty, hir_ty, param_span);
911+
}
912+
_ => {
913+
let mut diag = struct_span_err!(
914+
tcx.sess,
915+
hir_ty.span,
916+
E0741,
917+
"`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
918+
the type of a const parameter",
919+
non_structural_match_ty,
920+
);
921+
922+
if ty == non_structural_match_ty {
923+
diag.span_label(
924+
hir_ty.span,
925+
format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
926+
);
927+
}
928+
929+
diag.emit();
930+
}
931+
}
932+
}
933+
} else {
934+
let err_ty_str;
935+
let mut is_ptr = true;
936+
937+
let err = match ty.kind() {
938+
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None,
939+
ty::FnPtr(_) => Some("function pointers"),
940+
ty::RawPtr(_) => Some("raw pointers"),
941+
_ => {
942+
is_ptr = false;
943+
err_ty_str = format!("`{ty}`");
944+
Some(err_ty_str.as_str())
945+
}
946+
};
947+
948+
if let Some(unsupported_type) = err {
949+
if is_ptr {
950+
tcx.sess.span_err(
951+
hir_ty.span,
952+
&format!(
953+
"using {unsupported_type} as const generic parameters is forbidden",
954+
),
955+
);
956+
} else {
957+
let mut err = tcx.sess.struct_span_err(
958+
hir_ty.span,
959+
&format!(
960+
"{unsupported_type} is forbidden as the type of a const generic parameter",
961+
),
962+
);
963+
err.note("the only supported types are integers, `bool` and `char`");
964+
if tcx.sess.is_nightly_build() {
965+
err.help(
966+
"more complex types are supported with `#![feature(adt_const_params)]`",
967+
);
968+
}
969+
err.emit();
970+
}
971+
}
972+
}
973+
}
974+
849975
fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
850976
match param.kind {
851977
// We currently only check wf of const params here.
@@ -855,124 +981,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
855981
hir::GenericParamKind::Const { ty: hir_ty, default: _ } => {
856982
let ty = tcx.type_of(param.def_id);
857983

858-
if tcx.features().adt_const_params {
859-
if let Some(non_structural_match_ty) =
860-
traits::search_for_adt_const_param_violation(param.span, tcx, ty)
861-
{
862-
// We use the same error code in both branches, because this is really the same
863-
// issue: we just special-case the message for type parameters to make it
864-
// clearer.
865-
match non_structural_match_ty.kind() {
866-
ty::Param(_) => {
867-
// Const parameters may not have type parameters as their types,
868-
// because we cannot be sure that the type parameter derives `PartialEq`
869-
// and `Eq` (just implementing them is not enough for `structural_match`).
870-
struct_span_err!(
871-
tcx.sess,
872-
hir_ty.span,
873-
E0741,
874-
"`{ty}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \
875-
used as the type of a const parameter",
876-
)
877-
.span_label(
878-
hir_ty.span,
879-
format!("`{ty}` may not derive both `PartialEq` and `Eq`"),
880-
)
881-
.note(
882-
"it is not currently possible to use a type parameter as the type of a \
883-
const parameter",
884-
)
885-
.emit();
886-
}
887-
ty::Float(_) => {
888-
struct_span_err!(
889-
tcx.sess,
890-
hir_ty.span,
891-
E0741,
892-
"`{ty}` is forbidden as the type of a const generic parameter",
893-
)
894-
.note("floats do not derive `Eq` or `Ord`, which are required for const parameters")
895-
.emit();
896-
}
897-
ty::FnPtr(_) => {
898-
struct_span_err!(
899-
tcx.sess,
900-
hir_ty.span,
901-
E0741,
902-
"using function pointers as const generic parameters is forbidden",
903-
)
904-
.emit();
905-
}
906-
ty::RawPtr(_) => {
907-
struct_span_err!(
908-
tcx.sess,
909-
hir_ty.span,
910-
E0741,
911-
"using raw pointers as const generic parameters is forbidden",
912-
)
913-
.emit();
914-
}
915-
_ => {
916-
let mut diag = struct_span_err!(
917-
tcx.sess,
918-
hir_ty.span,
919-
E0741,
920-
"`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
921-
the type of a const parameter",
922-
non_structural_match_ty,
923-
);
924-
925-
if ty == non_structural_match_ty {
926-
diag.span_label(
927-
hir_ty.span,
928-
format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
929-
);
930-
}
931-
932-
diag.emit();
933-
}
934-
}
935-
}
936-
} else {
937-
let err_ty_str;
938-
let mut is_ptr = true;
939-
940-
let err = match ty.kind() {
941-
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None,
942-
ty::FnPtr(_) => Some("function pointers"),
943-
ty::RawPtr(_) => Some("raw pointers"),
944-
_ => {
945-
is_ptr = false;
946-
err_ty_str = format!("`{ty}`");
947-
Some(err_ty_str.as_str())
948-
}
949-
};
950-
951-
if let Some(unsupported_type) = err {
952-
if is_ptr {
953-
tcx.sess.span_err(
954-
hir_ty.span,
955-
&format!(
956-
"using {unsupported_type} as const generic parameters is forbidden",
957-
),
958-
);
959-
} else {
960-
let mut err = tcx.sess.struct_span_err(
961-
hir_ty.span,
962-
&format!(
963-
"{unsupported_type} is forbidden as the type of a const generic parameter",
964-
),
965-
);
966-
err.note("the only supported types are integers, `bool` and `char`");
967-
if tcx.sess.is_nightly_build() {
968-
err.help(
969-
"more complex types are supported with `#![feature(adt_const_params)]`",
970-
);
971-
}
972-
err.emit();
973-
}
974-
}
975-
}
984+
check_param_wf_ty(tcx, ty, hir_ty, param.span);
976985
}
977986
}
978987
}
+35-27
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,29 @@
11
use rustc_ast as ast;
22
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
3-
use rustc_middle::ty::{self, ParamEnv, ScalarInt, TyCtxt};
3+
use rustc_middle::ty::{self, ParamEnv, ScalarInt, Ty, TyCtxt};
44
use rustc_span::DUMMY_SP;
55

6-
pub(crate) fn lit_to_const<'tcx>(
7-
tcx: TyCtxt<'tcx>,
8-
lit_input: LitToConstInput<'tcx>,
9-
) -> Result<ty::Const<'tcx>, LitToConstError> {
10-
let LitToConstInput { lit, ty, neg } = lit_input;
11-
12-
let trunc = |n| {
13-
let param_ty = ParamEnv::reveal_all().and(ty);
14-
let width = tcx
15-
.layout_of(param_ty)
16-
.map_err(|_| {
17-
LitToConstError::Reported(tcx.sess.delay_span_bug(
18-
DUMMY_SP,
19-
format!("couldn't compute width of literal: {:?}", lit_input.lit),
20-
))
21-
})?
22-
.size;
23-
trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
24-
let result = width.truncate(n);
25-
trace!("trunc result: {}", result);
6+
fn trunc<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, lit: &ast::LitKind, n: u128) -> Result<ScalarInt, LitToConstError> {
7+
let param_ty = ParamEnv::reveal_all().and(ty);
8+
let width = tcx
9+
.layout_of(param_ty)
10+
.map_err(|_| {
11+
LitToConstError::Reported(tcx.sess.delay_span_bug(
12+
DUMMY_SP,
13+
format!("couldn't compute width of literal: {:?}", lit),
14+
))
15+
})?
16+
.size;
17+
trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
18+
let result = width.truncate(n);
19+
trace!("trunc result: {}", result);
2620

27-
Ok(ScalarInt::try_from_uint(result, width)
28-
.unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result)))
29-
};
21+
Ok(ScalarInt::try_from_uint(result, width)
22+
.unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result)))
23+
}
3024

31-
let valtree = match (lit, &ty.kind()) {
25+
fn get_valtree<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, neg: bool, lit: &ast::LitKind) -> Result<ty::ValTree<'tcx>, LitToConstError> {
26+
Ok(match (lit, &ty.kind()) {
3227
(ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
3328
let str_bytes = s.as_str().as_bytes();
3429
ty::ValTree::from_raw_bytes(tcx, str_bytes)
@@ -48,7 +43,7 @@ pub(crate) fn lit_to_const<'tcx>(
4843
}
4944
(ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => {
5045
let scalar_int =
51-
trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })?;
46+
trunc(tcx, ty, lit, if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })?;
5247
ty::ValTree::from_scalar_int(scalar_int)
5348
}
5449
(ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()),
@@ -58,8 +53,21 @@ pub(crate) fn lit_to_const<'tcx>(
5853
tcx.sess.delay_span_bug(DUMMY_SP, "encountered LitKind::Err during mir build"),
5954
));
6055
}
56+
(_, ty::Projection(ty::ProjectionTy { substs, item_def_id })) => {
57+
let binder_ty = tcx.bound_type_of(*item_def_id);
58+
let ty = binder_ty.subst(tcx, substs);
59+
get_valtree(tcx, ty, neg, lit)?
60+
}
6161
_ => return Err(LitToConstError::TypeError),
62-
};
62+
})
63+
}
64+
65+
pub(crate) fn lit_to_const<'tcx>(
66+
tcx: TyCtxt<'tcx>,
67+
lit_input: LitToConstInput<'tcx>,
68+
) -> Result<ty::Const<'tcx>, LitToConstError> {
69+
let LitToConstInput { lit, ty, neg } = lit_input;
6370

71+
let valtree = get_valtree(tcx, ty, neg, lit)?;
6472
Ok(ty::Const::from_value(tcx, valtree, ty))
6573
}

0 commit comments

Comments
 (0)