Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ declare_features! (
(internal, cfg_target_has_reliable_f16_f128, "1.88.0", None),
/// Allows identifying the `compiler_builtins` crate.
(internal, compiler_builtins, "1.13.0", None),
/// Allows skipping `ConstParamTy_` trait implementation checks
(internal, const_param_ty_unchecked, "CURRENT_RUSTC_VERSION", None),
/// Allows writing custom MIR
(internal, custom_mir, "1.65.0", None),
/// Implementation details of externally implementable items
Expand Down
29 changes: 19 additions & 10 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,12 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er
let span = tcx.def_span(param.def_id);
let def_id = param.def_id.expect_local();

if tcx.features().adt_const_params() || tcx.features().min_adt_const_params() {
if tcx.features().const_param_ty_unchecked() {
enter_wf_checking_ctxt(tcx, tcx.local_parent(def_id), |wfcx| {
wfcx.register_wf_obligation(span, None, ty.into());
Ok(())
})
} else if tcx.features().adt_const_params() || tcx.features().min_adt_const_params() {
enter_wf_checking_ctxt(tcx, tcx.local_parent(def_id), |wfcx| {
wfcx.register_bound(
ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(ty)),
Expand Down Expand Up @@ -904,7 +909,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er
) => None,
Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired) => {
Some(vec![
(adt_const_params_feature_string, sym::adt_const_params),
(adt_const_params_feature_string, sym::min_adt_const_params),
(
" references to implement the `ConstParamTy` trait".into(),
sym::unsized_const_params,
Expand All @@ -931,11 +936,13 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er

ty_is_local(ty).then_some(vec![(
adt_const_params_feature_string,
sym::adt_const_params,
sym::min_adt_const_params,
)])
}
// Implements `ConstParamTy`, suggest adding the feature to enable.
Ok(..) => Some(vec![(adt_const_params_feature_string, sym::adt_const_params)]),
Ok(..) => {
Some(vec![(adt_const_params_feature_string, sym::min_adt_const_params)])
}
};
if let Some(features) = may_suggest_feature {
tcx.disabled_nightly_features(&mut diag, features);
Expand Down Expand Up @@ -1346,12 +1353,14 @@ pub(super) fn check_type_const<'tcx>(
let tcx = wfcx.tcx();
let span = tcx.def_span(def_id);

wfcx.register_bound(
ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(item_ty)),
wfcx.param_env,
item_ty,
tcx.require_lang_item(LangItem::ConstParamTy, span),
);
if !tcx.features().const_param_ty_unchecked() {
wfcx.register_bound(
ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(item_ty)),
wfcx.param_env,
item_ty,
tcx.require_lang_item(LangItem::ConstParamTy, span),
);
}

if has_value {
let raw_ct = tcx.const_of_item(def_id).instantiate_identity();
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_hir_analysis/src/coherence/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), E
return Ok(());
}

if tcx.features().const_param_ty_unchecked() {
return Ok(());
}

if !tcx.features().adt_const_params() {
match *self_type.kind() {
ty::Adt(adt, _) if adt.is_struct() => {
Expand Down
111 changes: 88 additions & 23 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,24 +380,44 @@ pub trait GenericArgsLowerer<'a, 'tcx> {
) -> ty::GenericArg<'tcx>;
}

struct ForbidMCGParamUsesFolder<'tcx> {
/// Context in which `ForbidParamUsesFolder` is being used, to emit appropriate diagnostics.
enum ForbidParamContext {
/// Anon const in a const argument position.
ConstArgument,
/// Enum discriminant expression.
EnumDiscriminant,
}

struct ForbidParamUsesFolder<'tcx> {
tcx: TyCtxt<'tcx>,
anon_const_def_id: LocalDefId,
span: Span,
is_self_alias: bool,
context: ForbidParamContext,
}

impl<'tcx> ForbidMCGParamUsesFolder<'tcx> {
impl<'tcx> ForbidParamUsesFolder<'tcx> {
fn error(&self) -> ErrorGuaranteed {
let msg = if self.is_self_alias {
"generic `Self` types are currently not permitted in anonymous constants"
} else if self.tcx.features().generic_const_args() {
"generic parameters in const blocks are only allowed as the direct value of a `type const`"
} else {
"generic parameters may not be used in const operations"
let msg = match self.context {
ForbidParamContext::EnumDiscriminant if self.is_self_alias => {
"generic `Self` types are not permitted in enum discriminant values"
}
ForbidParamContext::EnumDiscriminant => {
"generic parameters may not be used in enum discriminant values"
}
ForbidParamContext::ConstArgument if self.is_self_alias => {
"generic `Self` types are currently not permitted in anonymous constants"
}
ForbidParamContext::ConstArgument => {
if self.tcx.features().generic_const_args() {
"generic parameters in const blocks are only allowed as the direct value of a `type const`"
} else {
"generic parameters may not be used in const operations"
}
}
};
let mut diag = self.tcx.dcx().struct_span_err(self.span, msg);
if self.is_self_alias {
if self.is_self_alias && matches!(self.context, ForbidParamContext::ConstArgument) {
let anon_const_hir_id: HirId = HirId::make_owner(self.anon_const_def_id);
let parent_impl = self.tcx.hir_parent_owner_iter(anon_const_hir_id).find_map(
|(_, node)| match node {
Expand All @@ -411,18 +431,20 @@ impl<'tcx> ForbidMCGParamUsesFolder<'tcx> {
diag.span_note(impl_.self_ty.span, "not a concrete type");
}
}
if self.tcx.features().min_generic_const_args() {
if matches!(self.context, ForbidParamContext::ConstArgument)
&& self.tcx.features().min_generic_const_args()
{
if !self.tcx.features().generic_const_args() {
diag.help("add `#![feature(generic_const_args)]` to allow generic expressions as the RHS of const items");
} else {
diag.help("consider factoring the expression into a `type const` item and use it as the const argument instead");
}
};
}
diag.emit()
}
}

impl<'tcx> ty::TypeFolder<TyCtxt<'tcx>> for ForbidMCGParamUsesFolder<'tcx> {
impl<'tcx> ty::TypeFolder<TyCtxt<'tcx>> for ForbidParamUsesFolder<'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.tcx
}
Expand Down Expand Up @@ -464,37 +486,80 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
&& tcx.def_kind(parent_def_id) == DefKind::AnonConst
&& let ty::AnonConstKind::MCG = tcx.anon_const_kind(parent_def_id)
{
let folder = ForbidMCGParamUsesFolder {
let folder = ForbidParamUsesFolder {
tcx,
anon_const_def_id: parent_def_id,
span,
is_self_alias: false,
context: ForbidParamContext::ConstArgument,
};
return Err(folder.error());
}
Ok(())
}

/// Returns the `ForbidParamContext` for the current anon const if it is a context that
/// forbids uses of generic parameters. `None` if the current item is not such a context.
///
/// Name resolution handles most invalid generic parameter uses in these contexts, but it
/// cannot reject `Self` that aliases a generic type, nor generic parameters introduced by
/// type-dependent name resolution (e.g. `<Self as Trait>::Assoc` resolving to a type that
/// contains params). Those cases are handled by `check_param_uses_if_mcg`.
fn anon_const_forbids_generic_params(&self) -> Option<ForbidParamContext> {
let tcx = self.tcx();
let parent_def_id = self.item_def_id();

// Inline consts and closures can be nested inside anon consts that forbid generic
// params (e.g. an enum discriminant). Walk up the def parent chain to find the
// nearest enclosing AnonConst and use that to determine the context.
let anon_const_def_id = match tcx.def_kind(parent_def_id) {
DefKind::AnonConst => parent_def_id,
DefKind::InlineConst | DefKind::Closure => {
let root = tcx.typeck_root_def_id(parent_def_id.into());
match tcx.def_kind(root) {
DefKind::AnonConst => root.expect_local(),
_ => return None,
}
}
_ => return None,
};

match tcx.anon_const_kind(anon_const_def_id) {
ty::AnonConstKind::MCG => Some(ForbidParamContext::ConstArgument),
ty::AnonConstKind::NonTypeSystem => {
// NonTypeSystem anon consts only have accessible generic parameters in specific
// positions (ty patterns and field defaults — see `generics_of`). In all other
// positions (e.g. enum discriminants) generic parameters are not in scope.
if tcx.generics_of(anon_const_def_id).count() == 0 {
Some(ForbidParamContext::EnumDiscriminant)
} else {
None
}
}
ty::AnonConstKind::GCE
| ty::AnonConstKind::GCA
| ty::AnonConstKind::RepeatExprCount => None,
}
}

/// Check for uses of generic parameters that are not in scope due to this being
/// in a non-generic anon const context.
/// in a non-generic anon const context (e.g. MCG or an enum discriminant).
///
/// Name resolution rejects most invalid uses, but cannot handle `Self` aliasing a
/// generic type or generic parameters introduced by type-dependent name resolution.
#[must_use = "need to use transformed output"]
fn check_param_uses_if_mcg<T>(&self, term: T, span: Span, is_self_alias: bool) -> T
where
T: ty::TypeFoldable<TyCtxt<'tcx>>,
{
let tcx = self.tcx();
let parent_def_id = self.item_def_id();
if tcx.def_kind(parent_def_id) == DefKind::AnonConst
&& let ty::AnonConstKind::MCG = tcx.anon_const_kind(parent_def_id)
if let Some(context) = self.anon_const_forbids_generic_params()
// Fast path if contains no params/escaping bound vars.
&& (term.has_param() || term.has_escaping_bound_vars())
{
let mut folder = ForbidMCGParamUsesFolder {
tcx,
anon_const_def_id: parent_def_id,
span,
is_self_alias,
};
let anon_const_def_id = self.item_def_id();
let mut folder =
ForbidParamUsesFolder { tcx, anon_const_def_id, span, is_self_alias, context };
term.fold_with(&mut folder)
} else {
term
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2377,8 +2377,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
continue;
}

// This pick is not a supertrait of the `child_pick`.
// Check if it's a subtrait of the `child_pick`, instead.
// This candidate is not a supertrait of the `child_trait`.
// Check if it's a subtrait of the `child_trait`, instead.
// If it is, then it must have been a subtrait of every
// other pick we've eliminated at this point. It will
// take over at this point.
Expand All @@ -2392,7 +2392,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
continue;
}

// `child_pick` is not a supertrait of this pick.
// Neither `child_trait` or the current candidate are
// supertraits of each other.
// Don't bail here, since we may be comparing two supertraits
// of a common subtrait. These two supertraits won't be related
// at all, but we will pick them up next round when we find their
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,7 @@ symbols! {
const_panic,
const_panic_fmt,
const_param_ty,
const_param_ty_unchecked,
const_precise_live_drops,
const_ptr_cast,
const_raw_ptr_deref,
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,11 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
if self.tcx().is_lang_item(def_id, LangItem::Sized) {
return Default::default();
}
if self.tcx().is_lang_item(def_id, LangItem::ConstParamTy)
&& self.tcx().features().const_param_ty_unchecked()
{
return Default::default();
}

let predicates = self.tcx().predicates_of(def_id);
let mut origins = vec![def_id; predicates.predicates.len()];
Expand Down
3 changes: 3 additions & 0 deletions library/core/src/char/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1932,6 +1932,9 @@ impl char {
/// U+0020 SPACE, U+0009 HORIZONTAL TAB, U+000A LINE FEED,
/// U+000C FORM FEED, or U+000D CARRIAGE RETURN.
///
/// **Warning:** Because the list above excludes U+000B VERTICAL TAB,
/// `c.is_ascii_whitespace()` is **not** equivalent to `c.is_ascii() && c.is_whitespace()`.
///
/// Rust uses the WhatWG Infra Standard's [definition of ASCII
/// whitespace][infra-aw]. There are several other definitions in
/// wide use. For instance, [the POSIX locale][pct] includes
Expand Down
6 changes: 5 additions & 1 deletion library/core/src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1095,9 +1095,13 @@ pub macro ConstParamTy($item:item) {
/* compiler built-in */
}

// For `adt_const_params` to be recognized as a feature
#[unstable(feature = "adt_const_params", issue = "95174")]
const _: () = ();

// FIXME(adt_const_params): handle `ty::FnDef`/`ty::Closure`
marker_impls! {
#[unstable(feature = "adt_const_params", issue = "95174")]
#[unstable(feature = "min_adt_const_params", issue = "154042")]
ConstParamTy_ for
usize, u8, u16, u32, u64, u128,
isize, i8, i16, i32, i64, i128,
Expand Down
3 changes: 3 additions & 0 deletions library/core/src/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,9 @@ impl u8 {
/// U+0020 SPACE, U+0009 HORIZONTAL TAB, U+000A LINE FEED,
/// U+000C FORM FEED, or U+000D CARRIAGE RETURN.
///
/// **Warning:** Because the list above excludes U+000B VERTICAL TAB,
/// `b.is_ascii_whitespace()` is **not** equivalent to `char::from(b).is_whitespace()`.
///
/// Rust uses the WhatWG Infra Standard's [definition of ASCII
/// whitespace][infra-aw]. There are several other definitions in
/// wide use. For instance, [the POSIX locale][pct] includes
Expand Down
18 changes: 15 additions & 3 deletions library/core/src/slice/ascii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,11 @@ impl [u8] {
/// Returns a byte slice with leading ASCII whitespace bytes removed.
///
/// 'Whitespace' refers to the definition used by
/// [`u8::is_ascii_whitespace`].
/// [`u8::is_ascii_whitespace`]. Importantly, this definition excludes
/// the `\0x0B` byte even though it has the Unicode [`White_Space`] property
/// and is removed by [`str::trim_start`].
///
/// [`White_Space`]: https://www.unicode.org/reports/tr44/#White_Space
///
/// # Examples
///
Expand Down Expand Up @@ -251,7 +255,11 @@ impl [u8] {
/// Returns a byte slice with trailing ASCII whitespace bytes removed.
///
/// 'Whitespace' refers to the definition used by
/// [`u8::is_ascii_whitespace`].
/// [`u8::is_ascii_whitespace`]. Importantly, this definition excludes
/// the `\0x0B` byte even though it has the Unicode [`White_Space`] property
/// and is removed by [`str::trim_end`].
///
/// [`White_Space`]: https://www.unicode.org/reports/tr44/#White_Space
///
/// # Examples
///
Expand Down Expand Up @@ -281,7 +289,11 @@ impl [u8] {
/// removed.
///
/// 'Whitespace' refers to the definition used by
/// [`u8::is_ascii_whitespace`].
/// [`u8::is_ascii_whitespace`]. Importantly, this definition excludes
/// the `\0x0B` byte even though it has the Unicode [`White_Space`] property
/// and is removed by [`str::trim`].
///
/// [`White_Space`]: https://www.unicode.org/reports/tr44/#White_Space
///
/// # Examples
///
Expand Down
Loading
Loading