From 291bc0db5104a881f0d9fe524335aaf64910a14b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 15 Nov 2022 13:57:53 +0100 Subject: [PATCH 1/4] Add normalization for projection types in const generics --- .../rustc_hir_analysis/src/check/wfcheck.rs | 245 +++++++++--------- compiler/rustc_mir_build/src/thir/constant.rs | 62 +++-- 2 files changed, 162 insertions(+), 145 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index efb34e4ff6592..31e810a4fbe36 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -846,6 +846,132 @@ 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_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, hir_ty: &rustc_hir::Ty<'_>, param_span: Span) { + 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(); + } + 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); + check_param_wf_ty(tcx, ty, hir_ty, param_span); + } + _ => { + 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 ty == non_structural_match_ty { + diag.span_label( + hir_ty.span, + format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), + ); + } + + diag.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(); + } + } + } +} + fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { match param.kind { // We currently only check wf of const params here. @@ -855,124 +981,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { hir::GenericParamKind::Const { ty: hir_ty, default: _ } => { let ty = tcx.type_of(param.def_id); - 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 ty == non_structural_match_ty { - diag.span_label( - hir_ty.span, - format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), - ); - } - - diag.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(); - } - } - } + check_param_wf_ty(tcx, ty, hir_ty, param.span); } } } diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index 85e8801bda3ec..cfe629f14128c 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -1,34 +1,29 @@ use rustc_ast as ast; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; -use rustc_middle::ty::{self, ParamEnv, ScalarInt, TyCtxt}; +use rustc_middle::ty::{self, ParamEnv, ScalarInt, Ty, TyCtxt}; use rustc_span::DUMMY_SP; -pub(crate) fn lit_to_const<'tcx>( - tcx: TyCtxt<'tcx>, - lit_input: LitToConstInput<'tcx>, -) -> Result, LitToConstError> { - let LitToConstInput { lit, ty, neg } = lit_input; - - 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_input.lit), - )) - })? - .size; - trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); - let result = width.truncate(n); - trace!("trunc result: {}", result); +fn trunc<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, lit: &ast::LitKind, n: u128) -> Result { + 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), + )) + })? + .size; + 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))) +} - let valtree = match (lit, &ty.kind()) { +fn get_valtree<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, neg: bool, lit: &ast::LitKind) -> Result, LitToConstError> { + Ok(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) @@ -48,7 +43,7 @@ pub(crate) fn lit_to_const<'tcx>( } (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { let scalar_int = - trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })?; + trunc(tcx, ty, lit, 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()), @@ -58,8 +53,21 @@ pub(crate) fn lit_to_const<'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, LitToConstError> { + let LitToConstInput { lit, ty, neg } = lit_input; + let valtree = get_valtree(tcx, ty, neg, lit)?; Ok(ty::Const::from_value(tcx, valtree, ty)) } From eed773803b4892df9f733b12a9c387b0d0755465 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 15 Nov 2022 13:58:08 +0100 Subject: [PATCH 2/4] Add test for projection in const generics --- .../const-generics/projection-as-arg-const.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/test/ui/const-generics/projection-as-arg-const.rs diff --git a/src/test/ui/const-generics/projection-as-arg-const.rs b/src/test/ui/const-generics/projection-as-arg-const.rs new file mode 100644 index 0000000000000..8688b648f3e79 --- /dev/null +++ b/src/test/ui/const-generics/projection-as-arg-const.rs @@ -0,0 +1,25 @@ +// run-pass + +#![allow(incomplete_features)] // This is needed for the `adt_const_params` feature. +#![feature(associated_type_defaults)] + +pub trait Identity: Sized { + type Identity = Self; + // ^ This is needed because otherwise we get: + // error: internal compiler error: compiler/rustc_hir_analysis/src/collect/type_of.rs:271:17: \ + // associated type missing default + // --> test.rs:6:5 + // | + // 6 | type Identity; + // | ^^^^^^^^^^^^^^ +} + +impl Identity for T {} + +pub fn foo::Identity>() { + assert!(X == 12); +} + +fn main() { + foo::<12>(); +} From 9587fd75c1be001f6297ad889f85ba9286663cfb Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 15 Nov 2022 15:47:39 +0100 Subject: [PATCH 3/4] Apply normalization for all projection for const generics --- .../rustc_hir_analysis/src/check/wfcheck.rs | 254 +++++++++--------- compiler/rustc_mir_build/src/thir/constant.rs | 40 ++- .../const-generics/projection-as-arg-const.rs | 16 +- 3 files changed, 157 insertions(+), 153 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 31e810a4fbe36..113e6ce96983e 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -846,132 +846,6 @@ 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_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, hir_ty: &rustc_hir::Ty<'_>, param_span: Span) { - 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(); - } - 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); - check_param_wf_ty(tcx, ty, hir_ty, param_span); - } - _ => { - 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 ty == non_structural_match_ty { - diag.span_label( - hir_ty.span, - format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), - ); - } - - diag.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(); - } - } - } -} - fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { match param.kind { // We currently only check wf of const params here. @@ -979,9 +853,133 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { // Const parameters are well formed if their type is structural match. hir::GenericParamKind::Const { ty: hir_ty, default: _ } => { - let ty = tcx.type_of(param.def_id); + 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); + } - check_param_wf_ty(tcx, ty, hir_ty, param.span); + 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(); + } + // Should have been normalized in + // `traits::search_for_adt_const_param_violation` + ty::Projection(_) => unreachable!(), + _ => { + 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 ty == non_structural_match_ty { + diag.span_label( + hir_ty.span, + format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), + ); + } + + diag.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(); + } + } + } } } } diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index cfe629f14128c..83940f69797f1 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -3,17 +3,22 @@ use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; use rustc_middle::ty::{self, ParamEnv, ScalarInt, Ty, TyCtxt}; use rustc_span::DUMMY_SP; -fn trunc<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, lit: &ast::LitKind, n: u128) -> Result { +fn trunc<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + lit: &ast::LitKind, + n: u128, +) -> Result { 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), - )) - })? - .size; + 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), + )) + })? + .size; trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); let result = width.truncate(n); trace!("trunc result: {}", result); @@ -22,7 +27,12 @@ fn trunc<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, lit: &ast::LitKind, n: u128) -> .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, LitToConstError> { +fn get_valtree<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + neg: bool, + lit: &ast::LitKind, +) -> Result, LitToConstError> { Ok(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(); @@ -42,8 +52,12 @@ fn get_valtree<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, neg: bool, lit: &ast::LitK 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( + tcx, + ty, + lit, + 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()), diff --git a/src/test/ui/const-generics/projection-as-arg-const.rs b/src/test/ui/const-generics/projection-as-arg-const.rs index 8688b648f3e79..66a82a80ac850 100644 --- a/src/test/ui/const-generics/projection-as-arg-const.rs +++ b/src/test/ui/const-generics/projection-as-arg-const.rs @@ -1,21 +1,13 @@ // run-pass -#![allow(incomplete_features)] // This is needed for the `adt_const_params` feature. -#![feature(associated_type_defaults)] +pub trait Identity { + type Identity; +} -pub trait Identity: Sized { +impl Identity for T { type Identity = Self; - // ^ This is needed because otherwise we get: - // error: internal compiler error: compiler/rustc_hir_analysis/src/collect/type_of.rs:271:17: \ - // associated type missing default - // --> test.rs:6:5 - // | - // 6 | type Identity; - // | ^^^^^^^^^^^^^^ } -impl Identity for T {} - pub fn foo::Identity>() { assert!(X == 12); } From 00d9f41cd31dd5d8fb5c657ea94a66d090796529 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 15 Nov 2022 18:26:59 +0100 Subject: [PATCH 4/4] Put normalization outside of lit_to_const query --- .../rustc_hir_analysis/src/check/wfcheck.rs | 234 +++++++++--------- compiler/rustc_middle/src/ty/consts.rs | 5 + compiler/rustc_mir_build/src/thir/constant.rs | 66 ++--- compiler/rustc_ty_utils/src/consts.rs | 21 +- 4 files changed, 155 insertions(+), 171 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 113e6ce96983e..0525af28a261a 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -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) } } } @@ -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(); - } - } - } + }); } } } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 37153a6394405..66d52d17e09e6 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -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( diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index 83940f69797f1..e76ca5899af79 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -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 { - let param_ty = ParamEnv::reveal_all().and(ty); - let width = - tcx.layout_of(param_ty) + lit_input: LitToConstInput<'tcx>, +) -> Result, 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, 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) @@ -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()), @@ -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, LitToConstError> { - let LitToConstInput { lit, ty, neg } = lit_input; + }; - let valtree = get_valtree(tcx, ty, neg, lit)?; Ok(ty::Const::from_value(tcx, valtree, ty)) } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index cb41c4f94e2e6..510d2457c3402 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -4,7 +4,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_index::vec::IndexVec; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; use rustc_middle::ty::abstract_const::{CastKind, Node, NodeId}; -use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, ParamEnv, TyCtxt, TypeVisitable}; use rustc_middle::{mir, thir}; use rustc_span::Span; use rustc_target::abi::VariantIdx; @@ -222,6 +222,9 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { fn recurse_build(&mut self, node: thir::ExprId) -> Result { use thir::ExprKind; let node = &self.body.exprs[node]; + let param_env = ParamEnv::reveal_all().with_reveal_all_normalized(self.tcx); + let node_ty = self.tcx.try_normalize_erasing_regions(param_env, node.ty).unwrap_or(node.ty); + Ok(match &node.kind { // I dont know if handling of these 3 is correct &ExprKind::Scope { value, .. } => self.recurse_build(value)?, @@ -231,12 +234,12 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { let sp = node.span; let constant = match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, - ty: node.ty, + ty: node_ty, neg, }) { Ok(c) => c, Err(LitToConstError::Reported(guar)) => { - self.tcx.const_error_with_guaranteed(node.ty, guar) + self.tcx.const_error_with_guaranteed(node_ty, guar) } Err(LitToConstError::TypeError) => { bug!("encountered type error in lit_to_const") @@ -247,23 +250,23 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { } &ExprKind::NonHirLiteral { lit, user_ty: _ } => { let val = ty::ValTree::from_scalar_int(lit); - self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) + self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node_ty))) } &ExprKind::ZstLiteral { user_ty: _ } => { let val = ty::ValTree::zst(); - self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) + self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node_ty))) } &ExprKind::NamedConst { def_id, substs, user_ty: _ } => { let uneval = ty::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs); - let constant = self.tcx.mk_const(ty::ConstKind::Unevaluated(uneval), node.ty); + let constant = self.tcx.mk_const(ty::ConstKind::Unevaluated(uneval), node_ty); self.nodes.push(Node::Leaf(constant)) } ExprKind::ConstParam { param, .. } => { - let const_param = self.tcx.mk_const(ty::ConstKind::Param(*param), node.ty); + let const_param = self.tcx.mk_const(ty::ConstKind::Param(*param), node_ty); self.nodes.push(Node::Leaf(const_param)) } @@ -308,11 +311,11 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested) &ExprKind::Use { source } => { let arg = self.recurse_build(source)?; - self.nodes.push(Node::Cast(CastKind::Use, arg, node.ty)) + self.nodes.push(Node::Cast(CastKind::Use, arg, node_ty)) } &ExprKind::Cast { source } => { let arg = self.recurse_build(source)?; - self.nodes.push(Node::Cast(CastKind::As, arg, node.ty)) + self.nodes.push(Node::Cast(CastKind::As, arg, node_ty)) } ExprKind::Borrow { arg, .. } => { let arg_node = &self.body.exprs[*arg];