Skip to content

Commit 9629b90

Browse files
committed
Auto merge of #127722 - BoxyUwU:new_adt_const_params_limitations, r=compiler-errors
Forbid borrows and unsized types from being used as the type of a const generic under `adt_const_params` Fixes #112219 Fixes #112124 Fixes #112125 ### Motivation Currently the `adt_const_params` feature allows writing `Foo<const N: [u8]>` this is entirely useless as it is not possible to write an expression which evaluates to a type that is not `Sized`. In order to actually use unsized types in const generics they are typically written as `const N: &[u8]` which *is* possible to provide a value of. Unfortunately allowing the types of const parameters to contain references is non trivial (#120961) as it introduces a number of difficult questions about how equality of references in the type system should behave. References in the types of const generics is largely only useful for using unsized types in const generics. This PR introduces a new feature gate `unsized_const_parameters` and moves support for `const N: [u8]` and `const N: &...` from `adt_const_params` into it. The goal here hopefully is to experiment with allowing `const N: [u8]` to work without references and then eventually completely forbid references in const generics. Splitting this out into a new feature gate means that stabilization of `adt_const_params` does not have to resolve #120961 which is the only remaining "big" blocker for the feature. Remaining issues after this are a few ICEs and naming bikeshed for `ConstParamTy`. ### Implementation The implementation is slightly subtle here as we would like to ensure that a stabilization of `adt_const_params` is forwards compatible with any outcome of `unsized_const_parameters`. This is inherently tricky as we do not support unstable trait implementations and we determine whether a type is valid as the type of a const parameter via a trait bound. There are a few constraints here: - We would like to *allow for the possibility* of adding a `Sized` supertrait to `ConstParamTy` in the event that we wind up opting to not support unsized types and instead requiring people to write the 'sized version', e.g. `const N: [u8; M]` instead of `const N: [u8]`. - Crates should be able to enable `unsized_const_parameters` and write trait implementations of `ConstParamTy` for `!Sized` types without downstream crates that only enable `adt_const_params` being able to observe this (required for std to be able to `impl<T> ConstParamTy for [T]` Ultimately the way this is accomplished is via having two traits (sad), `ConstParamTy` and `UnsizedConstParamTy`. Depending on whether `unsized_const_parameters` is enabled or not we change which trait is used to check whether a type is allowed to be a const parameter. Long term (when stabilizing `UnsizedConstParamTy`) it should be possible to completely merge these traits (and derive macros), only having a single `trait ConstParamTy` and `macro ConstParamTy`. Under `adt_const_params` it is now illegal to directly refer to `ConstParamTy` it is only used as an internal impl detail by `derive(ConstParamTy)` and checking const parameters are well formed. This is necessary in order to ensure forwards compatibility with all possible future directions for `feature(unsized_const_parameters)`. Generally the intuition here should be that `ConstParamTy` is the stable trait that everything uses, and `UnsizedConstParamTy` is that plus unstable implementations (well, I suppose `ConstParamTy` isn't stable yet :P).
2 parents a62ac15 + d0c11bf commit 9629b90

File tree

141 files changed

+1242
-543
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

141 files changed

+1242
-543
lines changed

compiler/rustc_builtin_macros/src/deriving/bounds.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,44 @@ pub(crate) fn expand_deriving_const_param_ty(
3838
) {
3939
let trait_def = TraitDef {
4040
span,
41-
path: path_std!(marker::ConstParamTy),
41+
path: path_std!(marker::ConstParamTy_),
42+
skip_path_as_bound: false,
43+
needs_copy_as_bound_if_packed: false,
44+
additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))],
45+
supports_unions: false,
46+
methods: Vec::new(),
47+
associated_types: Vec::new(),
48+
is_const,
49+
};
50+
51+
trait_def.expand(cx, mitem, item, push);
52+
53+
let trait_def = TraitDef {
54+
span,
55+
path: path_std!(marker::UnsizedConstParamTy),
56+
skip_path_as_bound: false,
57+
needs_copy_as_bound_if_packed: false,
58+
additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))],
59+
supports_unions: false,
60+
methods: Vec::new(),
61+
associated_types: Vec::new(),
62+
is_const,
63+
};
64+
65+
trait_def.expand(cx, mitem, item, push);
66+
}
67+
68+
pub(crate) fn expand_deriving_unsized_const_param_ty(
69+
cx: &ExtCtxt<'_>,
70+
span: Span,
71+
mitem: &MetaItem,
72+
item: &Annotatable,
73+
push: &mut dyn FnMut(Annotatable),
74+
is_const: bool,
75+
) {
76+
let trait_def = TraitDef {
77+
span,
78+
path: path_std!(marker::UnsizedConstParamTy),
4279
skip_path_as_bound: false,
4380
needs_copy_as_bound_if_packed: false,
4481
additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))],

compiler/rustc_builtin_macros/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
118118
Clone: clone::expand_deriving_clone,
119119
Copy: bounds::expand_deriving_copy,
120120
ConstParamTy: bounds::expand_deriving_const_param_ty,
121+
UnsizedConstParamTy: bounds::expand_deriving_unsized_const_param_ty,
121122
Debug: debug::expand_deriving_debug,
122123
Default: default::expand_deriving_default,
123124
Eq: eq::expand_deriving_eq,

compiler/rustc_error_codes/src/error_codes/E0771.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ allowed.
66
Erroneous code example:
77

88
```compile_fail,E0770
9-
#![feature(adt_const_params)]
9+
#![feature(adt_const_params, unsized_const_params)]
1010
1111
fn function_with_str<'a, const STRING: &'a str>() {} // error!
1212
```
@@ -15,7 +15,7 @@ To fix this issue, the lifetime in the const generic need to be changed to
1515
`'static`:
1616

1717
```
18-
#![feature(adt_const_params)]
18+
#![feature(adt_const_params, unsized_const_params)]
1919
2020
fn function_with_str<const STRING: &'static str>() {} // ok!
2121
```

compiler/rustc_feature/src/unstable.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,8 @@ declare_features! (
339339
(unstable, abi_riscv_interrupt, "1.73.0", Some(111889)),
340340
/// Allows `extern "x86-interrupt" fn()`.
341341
(unstable, abi_x86_interrupt, "1.17.0", Some(40180)),
342-
/// Allows additional const parameter types, such as `&'static str` or user defined types
343-
(incomplete, adt_const_params, "1.56.0", Some(95174)),
342+
/// Allows additional const parameter types, such as `[u8; 10]` or user defined types
343+
(unstable, adt_const_params, "1.56.0", Some(95174)),
344344
/// Allows defining an `#[alloc_error_handler]`.
345345
(unstable, alloc_error_handler, "1.29.0", Some(51540)),
346346
/// Allows trait methods with arbitrary self types.
@@ -630,6 +630,9 @@ declare_features! (
630630
(unstable, unsafe_attributes, "1.80.0", Some(123757)),
631631
/// Allows unsafe on extern declarations and safety qualifiers over internal items.
632632
(unstable, unsafe_extern_blocks, "1.80.0", Some(123743)),
633+
/// Allows const generic parameters to be defined with types that
634+
/// are not `Sized`, e.g. `fn foo<const N: [u8]>() {`.
635+
(incomplete, unsized_const_params, "CURRENT_RUSTC_VERSION", Some(95174)),
633636
/// Allows unsized fn parameters.
634637
(internal, unsized_fn_params, "1.49.0", Some(48055)),
635638
/// Allows unsized rvalues at arguments and parameters.

compiler/rustc_hir/src/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ language_item_table! {
358358
PointerLike, sym::pointer_like, pointer_like, Target::Trait, GenericRequirement::Exact(0);
359359

360360
ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
361+
UnsizedConstParamTy, sym::unsized_const_param_ty, unsized_const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
361362

362363
Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None;
363364
PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;

compiler/rustc_hir_analysis/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ hir_analysis_const_param_ty_impl_on_non_adt =
9999
the trait `ConstParamTy` may not be implemented for this type
100100
.label = type is not a structure or enumeration
101101
102+
hir_analysis_const_param_ty_impl_on_unsized =
103+
the trait `ConstParamTy` may not be implemented for this type
104+
.label = type is not `Sized`
105+
102106
hir_analysis_const_specialize = cannot specialize on const impl with non-const impl
103107
104108
hir_analysis_copy_impl_on_non_adt =

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+39-16
Original file line numberDiff line numberDiff line change
@@ -922,10 +922,8 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
922922
} => {
923923
let ty = tcx.type_of(param.def_id).instantiate_identity();
924924

925-
if tcx.features().adt_const_params {
925+
if tcx.features().unsized_const_params {
926926
enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| {
927-
let trait_def_id =
928-
tcx.require_lang_item(LangItem::ConstParamTy, Some(hir_ty.span));
929927
wfcx.register_bound(
930928
ObligationCause::new(
931929
hir_ty.span,
@@ -934,7 +932,21 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
934932
),
935933
wfcx.param_env,
936934
ty,
937-
trait_def_id,
935+
tcx.require_lang_item(LangItem::UnsizedConstParamTy, Some(hir_ty.span)),
936+
);
937+
Ok(())
938+
})
939+
} else if tcx.features().adt_const_params {
940+
enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| {
941+
wfcx.register_bound(
942+
ObligationCause::new(
943+
hir_ty.span,
944+
param.def_id,
945+
ObligationCauseCode::ConstParam(ty),
946+
),
947+
wfcx.param_env,
948+
ty,
949+
tcx.require_lang_item(LangItem::ConstParamTy, Some(hir_ty.span)),
938950
);
939951
Ok(())
940952
})
@@ -958,14 +970,29 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
958970
diag.note("the only supported types are integers, `bool` and `char`");
959971

960972
let cause = ObligationCause::misc(hir_ty.span, param.def_id);
973+
let adt_const_params_feature_string =
974+
" more complex and user defined types".to_string();
961975
let may_suggest_feature = match type_allowed_to_implement_const_param_ty(
962976
tcx,
963977
tcx.param_env(param.def_id),
964978
ty,
979+
LangItem::ConstParamTy,
965980
cause,
966981
) {
967982
// Can never implement `ConstParamTy`, don't suggest anything.
968-
Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => false,
983+
Err(
984+
ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed
985+
| ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(..),
986+
) => None,
987+
Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired) => {
988+
Some(vec![
989+
(adt_const_params_feature_string, sym::adt_const_params),
990+
(
991+
" references to implement the `ConstParamTy` trait".into(),
992+
sym::unsized_const_params,
993+
),
994+
])
995+
}
969996
// May be able to implement `ConstParamTy`. Only emit the feature help
970997
// if the type is local, since the user may be able to fix the local type.
971998
Err(ConstParamTyImplementationError::InfrigingFields(..)) => {
@@ -985,20 +1012,16 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
9851012
}
9861013
}
9871014

988-
ty_is_local(ty)
1015+
ty_is_local(ty).then_some(vec![(
1016+
adt_const_params_feature_string,
1017+
sym::adt_const_params,
1018+
)])
9891019
}
9901020
// Implments `ConstParamTy`, suggest adding the feature to enable.
991-
Ok(..) => true,
1021+
Ok(..) => Some(vec![(adt_const_params_feature_string, sym::adt_const_params)]),
9921022
};
993-
if may_suggest_feature {
994-
tcx.disabled_nightly_features(
995-
&mut diag,
996-
Some(param.hir_id),
997-
[(
998-
" more complex and user defined types".to_string(),
999-
sym::adt_const_params,
1000-
)],
1001-
);
1023+
if let Some(features) = may_suggest_feature {
1024+
tcx.disabled_nightly_features(&mut diag, Some(param.hir_id), features);
10021025
}
10031026

10041027
Err(diag.emit())

compiler/rustc_hir_analysis/src/coherence/builtin.rs

+48-13
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,13 @@ pub(super) fn check_trait<'tcx>(
3636
let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header };
3737
let mut res = checker.check(lang_items.drop_trait(), visit_implementation_of_drop);
3838
res = res.and(checker.check(lang_items.copy_trait(), visit_implementation_of_copy));
39-
res = res.and(
40-
checker.check(lang_items.const_param_ty_trait(), visit_implementation_of_const_param_ty),
41-
);
39+
res = res.and(checker.check(lang_items.const_param_ty_trait(), |checker| {
40+
visit_implementation_of_const_param_ty(checker, LangItem::ConstParamTy)
41+
}));
42+
res = res.and(checker.check(lang_items.unsized_const_param_ty_trait(), |checker| {
43+
visit_implementation_of_const_param_ty(checker, LangItem::UnsizedConstParamTy)
44+
}));
45+
4246
res = res.and(
4347
checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized),
4448
);
@@ -103,7 +107,13 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
103107
Ok(()) => Ok(()),
104108
Err(CopyImplementationError::InfringingFields(fields)) => {
105109
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
106-
Err(infringing_fields_error(tcx, fields, LangItem::Copy, impl_did, span))
110+
Err(infringing_fields_error(
111+
tcx,
112+
fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)),
113+
LangItem::Copy,
114+
impl_did,
115+
span,
116+
))
107117
}
108118
Err(CopyImplementationError::NotAnAdt) => {
109119
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
@@ -116,7 +126,12 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
116126
}
117127
}
118128

119-
fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
129+
fn visit_implementation_of_const_param_ty(
130+
checker: &Checker<'_>,
131+
kind: LangItem,
132+
) -> Result<(), ErrorGuaranteed> {
133+
assert!(matches!(kind, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy));
134+
120135
let tcx = checker.tcx;
121136
let header = checker.impl_header;
122137
let impl_did = checker.impl_def_id;
@@ -125,21 +140,41 @@ fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), E
125140

126141
let param_env = tcx.param_env(impl_did);
127142

128-
if let ty::ImplPolarity::Negative = header.polarity {
143+
if let ty::ImplPolarity::Negative | ty::ImplPolarity::Reservation = header.polarity {
129144
return Ok(());
130145
}
131146

132147
let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did);
133-
match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) {
148+
match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, kind, cause) {
134149
Ok(()) => Ok(()),
135150
Err(ConstParamTyImplementationError::InfrigingFields(fields)) => {
136151
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
137-
Err(infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span))
152+
Err(infringing_fields_error(
153+
tcx,
154+
fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)),
155+
LangItem::ConstParamTy,
156+
impl_did,
157+
span,
158+
))
138159
}
139160
Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => {
140161
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
141162
Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span }))
142163
}
164+
Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(infringing_tys)) => {
165+
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
166+
Err(infringing_fields_error(
167+
tcx,
168+
infringing_tys.into_iter().map(|(ty, reason)| (span, ty, reason)),
169+
LangItem::ConstParamTy,
170+
impl_did,
171+
span,
172+
))
173+
}
174+
Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired) => {
175+
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
176+
Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnUnsized { span }))
177+
}
143178
}
144179
}
145180

@@ -501,9 +536,9 @@ pub fn coerce_unsized_info<'tcx>(
501536
Ok(CoerceUnsizedInfo { custom_kind: kind })
502537
}
503538

504-
fn infringing_fields_error(
505-
tcx: TyCtxt<'_>,
506-
fields: Vec<(&ty::FieldDef, Ty<'_>, InfringingFieldsReason<'_>)>,
539+
fn infringing_fields_error<'tcx>(
540+
tcx: TyCtxt<'tcx>,
541+
infringing_tys: impl Iterator<Item = (Span, Ty<'tcx>, InfringingFieldsReason<'tcx>)>,
507542
lang_item: LangItem,
508543
impl_did: LocalDefId,
509544
impl_span: Span,
@@ -521,13 +556,13 @@ fn infringing_fields_error(
521556

522557
let mut label_spans = Vec::new();
523558

524-
for (field, ty, reason) in fields {
559+
for (span, ty, reason) in infringing_tys {
525560
// Only report an error once per type.
526561
if !seen_tys.insert(ty) {
527562
continue;
528563
}
529564

530-
label_spans.push(tcx.def_span(field.did));
565+
label_spans.push(span);
531566

532567
match reason {
533568
InfringingFieldsReason::Fulfill(fulfillment_errors) => {

compiler/rustc_hir_analysis/src/errors.rs

+8
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,14 @@ pub struct CopyImplOnNonAdt {
278278
pub span: Span,
279279
}
280280

281+
#[derive(Diagnostic)]
282+
#[diag(hir_analysis_const_param_ty_impl_on_unsized)]
283+
pub struct ConstParamTyImplOnUnsized {
284+
#[primary_span]
285+
#[label]
286+
pub span: Span,
287+
}
288+
281289
#[derive(Diagnostic)]
282290
#[diag(hir_analysis_const_param_ty_impl_on_non_adt)]
283291
pub struct ConstParamTyImplOnNonAdt {

compiler/rustc_span/src/symbol.rs

+4
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ symbols! {
177177
CoerceUnsized,
178178
Command,
179179
ConstParamTy,
180+
ConstParamTy_,
180181
Context,
181182
Continue,
182183
Copy,
@@ -336,6 +337,7 @@ symbols! {
336337
TyKind,
337338
Unknown,
338339
Unsize,
340+
UnsizedConstParamTy,
339341
Upvars,
340342
Vec,
341343
VecDeque,
@@ -2001,6 +2003,8 @@ symbols! {
20012003
unsafe_no_drop_flag,
20022004
unsafe_pin_internals,
20032005
unsize,
2006+
unsized_const_param_ty,
2007+
unsized_const_params,
20042008
unsized_fn_params,
20052009
unsized_locals,
20062010
unsized_tuple_coercion,

0 commit comments

Comments
 (0)