diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 67007523a0671..cfb798b31ea0b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -45,6 +45,38 @@ use crate::method::{self, MethodCallee}; use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Transform generic args for inherent associated type constants (IACs). + /// + /// IACs have a different generic parameter structure than regular associated constants: + /// - Regular assoc const: parent (impl) generic params + own generic params + /// - IAC (type_const): Self type + own generic params + pub(crate) fn transform_args_for_inherent_type_const( + &self, + def_id: DefId, + args: GenericArgsRef<'tcx>, + ) -> GenericArgsRef<'tcx> { + let tcx = self.tcx; + if !tcx.is_type_const(def_id) { + return args; + } + let Some(assoc_item) = tcx.opt_associated_item(def_id) else { + return args; + }; + if !matches!(assoc_item.container, ty::AssocContainer::InherentImpl) { + return args; + } + + let impl_def_id = assoc_item.container_id(tcx); + let generics = tcx.generics_of(def_id); + let impl_args = &args[..generics.parent_count]; + let self_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args); + // Build new args: [Self, own_args...] + let own_args = &args[generics.parent_count..]; + tcx.mk_args_from_iter( + std::iter::once(ty::GenericArg::from(self_ty)).chain(own_args.iter().copied()), + ) + } + /// Produces warning on the given node, if the current point in the /// function is unreachable, and there hasn't been another warning. pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) { @@ -1281,8 +1313,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }); + let args_for_user_type = if let Res::Def(DefKind::AssocConst, def_id) = res { + self.transform_args_for_inherent_type_const(def_id, args_raw) + } else { + args_raw + }; + // First, store the "user args" for later. - self.write_user_type_annotation_from_args(hir_id, def_id, args_raw, user_self_ty); + self.write_user_type_annotation_from_args(hir_id, def_id, args_for_user_type, user_self_ty); // Normalize only after registering type annotations. let args = self.normalize(span, args_raw); @@ -1322,6 +1360,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_instantiated); + + let args = if let Res::Def(DefKind::AssocConst, def_id) = res { + self.transform_args_for_inherent_type_const(def_id, args) + } else { + args + }; + self.write_args(hir_id, args); (ty_instantiated, res) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 41e1388e31464..3de4d14c915ad 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2915,12 +2915,15 @@ impl<'tcx> TyCtxt<'tcx> { ) -> bool { let generics = self.generics_of(def_id); - // IATs themselves have a weird arg setup (self + own args), but nested items *in* IATs - // (namely: opaques, i.e. ATPITs) do not. - let own_args = if !nested - && let DefKind::AssocTy = self.def_kind(def_id) - && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) - { + // IATs and IACs (inherent associated types/consts with #[type_const]) themselves have a + // weird arg setup (self + own args), but nested items *in* IATs (namely: opaques, i.e. + // ATPITs) do not. + let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy) + && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); + let is_inherent_assoc_type_const = matches!(self.def_kind(def_id), DefKind::AssocConst) + && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }) + && self.is_type_const(def_id); + let own_args = if !nested && (is_inherent_assoc_ty || is_inherent_assoc_type_const) { if generics.own_params.len() + 1 != args.len() { return false; } @@ -2962,9 +2965,12 @@ impl<'tcx> TyCtxt<'tcx> { /// and print out the args if not. pub fn debug_assert_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) { if cfg!(debug_assertions) && !self.check_args_compatible(def_id, args) { - if let DefKind::AssocTy = self.def_kind(def_id) - && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) - { + let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy) + && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); + let is_inherent_assoc_type_const = matches!(self.def_kind(def_id), DefKind::AssocConst) + && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }) + && self.is_type_const(def_id); + if is_inherent_assoc_ty || is_inherent_assoc_type_const { bug!( "args not compatible with generics for {}: args={:#?}, generics={:#?}", self.def_path_str(def_id), diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 78e7aef78f1bc..91a0251318550 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -1,3 +1,4 @@ +use rustc_hir::def::DefKind; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::traits::Obligation; use rustc_middle::traits::query::NoSolution; @@ -96,6 +97,26 @@ fn relate_mir_and_user_args<'tcx>( let tcx = ocx.infcx.tcx; let cause = ObligationCause::dummy_with_span(span); + // For IACs, the user args are in the format [SelfTy, GAT_args...] but type_of expects [impl_args..., GAT_args...]. + // We need to infer the impl args by equating the impl's self type with the user-provided self type. + let is_inherent_assoc_const = tcx.def_kind(def_id) == DefKind::AssocConst + && tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false } + && tcx.is_type_const(def_id); + + let args = if is_inherent_assoc_const { + let impl_def_id = tcx.parent(def_id); + let impl_args = ocx.infcx.fresh_args_for_item(span, impl_def_id); + let impl_self_ty = + ocx.normalize(&cause, param_env, tcx.type_of(impl_def_id).instantiate(tcx, impl_args)); + let user_self_ty = ocx.normalize(&cause, param_env, args[0].expect_ty()); + ocx.eq(&cause, param_env, impl_self_ty, user_self_ty)?; + + let gat_args = &args[1..]; + tcx.mk_args_from_iter(impl_args.iter().chain(gat_args.iter().copied())) + } else { + args + }; + let ty = tcx.type_of(def_id).instantiate(tcx, args); let ty = ocx.normalize(&cause, param_env, ty); debug!("relate_type_and_user_type: ty of def-id is {:?}", ty); diff --git a/tests/crashes/138089.rs b/tests/crashes/138089.rs deleted file mode 100644 index f4864971ae5d1..0000000000000 --- a/tests/crashes/138089.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ known-bug: #138089 -//@ needs-rustc-debug-assertions - -#![feature(generic_const_exprs)] -#![feature(min_generic_const_args)] -#![feature(inherent_associated_types)] -struct OnDiskDirEntry<'a> {} - -impl<'a> OnDiskDirEntry<'a> { - #[type_const] - const LFN_FRAGMENT_LEN: i64 = 2; - - fn lfn_contents() -> [char; Self::LFN_FRAGMENT_LEN] { - loop {} - } -} diff --git a/tests/crashes/138226-2.rs b/tests/crashes/138226-2.rs deleted file mode 100644 index a2ebbdefdf3f3..0000000000000 --- a/tests/crashes/138226-2.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: #138226 -//@ needs-rustc-debug-assertions -#![feature(min_generic_const_args)] -#![feature(inherent_associated_types)] -struct Bar; -impl Bar { - #[type_const] - const LEN: usize = 4; - - fn bar() { - let _ = [0; Self::LEN]; - } -} diff --git a/tests/crashes/138226.rs b/tests/crashes/138226.rs deleted file mode 100644 index 7d13461a56b35..0000000000000 --- a/tests/crashes/138226.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: #138226 -//@ needs-rustc-debug-assertions -#![feature(min_generic_const_args)] -#![feature(inherent_associated_types)] -struct Foo(A, B); -impl Foo { - #[type_const] - const LEN: usize = 4; - - fn foo() { - let _ = [5; Self::LEN]; - } -} diff --git a/tests/crashes/150960.rs b/tests/crashes/150960.rs deleted file mode 100644 index 2d46eea679899..0000000000000 --- a/tests/crashes/150960.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ known-bug: #150960 -#![feature(min_generic_const_args)] -struct Baz; -impl Baz { - #[type_const] - const LEN: usize = 4; - - fn baz() { - let _ = [0; const { Self::LEN }]; - } -} diff --git a/tests/ui/associated-consts/type-const-in-array-len-wrong-type.rs b/tests/ui/associated-consts/type-const-in-array-len-wrong-type.rs new file mode 100644 index 0000000000000..1a37f5957f25c --- /dev/null +++ b/tests/ui/associated-consts/type-const-in-array-len-wrong-type.rs @@ -0,0 +1,20 @@ +#![feature(generic_const_exprs)] +//~^ WARN the feature `generic_const_exprs` is incomplete +#![feature(min_generic_const_args)] +//~^ WARN the feature `min_generic_const_args` is incomplete +#![feature(inherent_associated_types)] +//~^ WARN the feature `inherent_associated_types` is incomplete + +struct OnDiskDirEntry<'a>(&'a ()); + +impl<'a> OnDiskDirEntry<'a> { + #[type_const] + const LFN_FRAGMENT_LEN: i64 = 2; + + fn lfn_contents() -> [char; Self::LFN_FRAGMENT_LEN] { + //~^ ERROR the constant `2` is not of type `usize` + loop {} + } +} + +fn main() {} diff --git a/tests/ui/associated-consts/type-const-in-array-len-wrong-type.stderr b/tests/ui/associated-consts/type-const-in-array-len-wrong-type.stderr new file mode 100644 index 0000000000000..607b7789ae677 --- /dev/null +++ b/tests/ui/associated-consts/type-const-in-array-len-wrong-type.stderr @@ -0,0 +1,35 @@ +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-const-in-array-len-wrong-type.rs:1:12 + | +LL | #![feature(generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #76560 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: the feature `min_generic_const_args` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-const-in-array-len-wrong-type.rs:3:12 + | +LL | #![feature(min_generic_const_args)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #132980 for more information + +warning: the feature `inherent_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-const-in-array-len-wrong-type.rs:5:12 + | +LL | #![feature(inherent_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #8995 for more information + +error: the constant `2` is not of type `usize` + --> $DIR/type-const-in-array-len-wrong-type.rs:14:26 + | +LL | fn lfn_contents() -> [char; Self::LFN_FRAGMENT_LEN] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `i64` + | + = note: the length of array `[char; 2]` must be type `usize` + +error: aborting due to 1 previous error; 3 warnings emitted + diff --git a/tests/ui/associated-consts/type-const-in-array-len.rs b/tests/ui/associated-consts/type-const-in-array-len.rs new file mode 100644 index 0000000000000..69dd3f443b874 --- /dev/null +++ b/tests/ui/associated-consts/type-const-in-array-len.rs @@ -0,0 +1,41 @@ +//@ check-pass + +#![feature(min_generic_const_args)] +//~^ WARN the feature `min_generic_const_args` is incomplete +#![feature(inherent_associated_types)] +//~^ WARN the feature `inherent_associated_types` is incomplete + +// Test case from #138226: generic impl with multiple type parameters +struct Foo(A, B); +impl Foo { + #[type_const] + const LEN: usize = 4; + + fn foo() { + let _ = [5; Self::LEN]; + } +} + +// Test case from #138226: generic impl with const parameter +struct Bar; +impl Bar { + #[type_const] + const LEN: usize = 4; + + fn bar() { + let _ = [0; Self::LEN]; + } +} + +// Test case from #150960: non-generic impl with const block +struct Baz; +impl Baz { + #[type_const] + const LEN: usize = 4; + + fn baz() { + let _ = [0; { Self::LEN }]; + } +} + +fn main() {} diff --git a/tests/ui/associated-consts/type-const-in-array-len.stderr b/tests/ui/associated-consts/type-const-in-array-len.stderr new file mode 100644 index 0000000000000..546995d13a0a8 --- /dev/null +++ b/tests/ui/associated-consts/type-const-in-array-len.stderr @@ -0,0 +1,19 @@ +warning: the feature `min_generic_const_args` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-const-in-array-len.rs:3:12 + | +LL | #![feature(min_generic_const_args)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #132980 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: the feature `inherent_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-const-in-array-len.rs:5:12 + | +LL | #![feature(inherent_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #8995 for more information + +warning: 2 warnings emitted + diff --git a/tests/ui/associated-types/type-const-inherent-impl-normalize.rs b/tests/ui/associated-types/type-const-inherent-impl-normalize.rs new file mode 100644 index 0000000000000..70eea70db4c68 --- /dev/null +++ b/tests/ui/associated-types/type-const-inherent-impl-normalize.rs @@ -0,0 +1,16 @@ +struct S; +impl S { + #[type_const] + //~^ ERROR: the `#[type_const]` attribute is an experimental feature + const LEN: usize = 1; + fn arr() { + [8; Self::LEN] + //~^ WARN: cannot use constants which depend on generic parameters in types + //~| WARN: this was previously accepted by the compiler but is being phased out + //~| WARN: cannot use constants which depend on generic parameters in types + //~| WARN: this was previously accepted by the compiler but is being phased out + //~| ERROR: mismatched types + } +} + +pub fn main() {} diff --git a/tests/ui/associated-types/type-const-inherent-impl-normalize.stderr b/tests/ui/associated-types/type-const-inherent-impl-normalize.stderr new file mode 100644 index 0000000000000..958d38e855eaf --- /dev/null +++ b/tests/ui/associated-types/type-const-inherent-impl-normalize.stderr @@ -0,0 +1,42 @@ +error[E0658]: the `#[type_const]` attribute is an experimental feature + --> $DIR/type-const-inherent-impl-normalize.rs:3:5 + | +LL | #[type_const] + | ^^^^^^^^^^^^^ + | + = note: see issue #132980 for more information + = help: add `#![feature(min_generic_const_args)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: cannot use constants which depend on generic parameters in types + --> $DIR/type-const-inherent-impl-normalize.rs:7:13 + | +LL | [8; Self::LEN] + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #76200 + = note: `#[warn(const_evaluatable_unchecked)]` (part of `#[warn(future_incompatible)]`) on by default + +warning: cannot use constants which depend on generic parameters in types + --> $DIR/type-const-inherent-impl-normalize.rs:7:13 + | +LL | [8; Self::LEN] + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #76200 + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0308]: mismatched types + --> $DIR/type-const-inherent-impl-normalize.rs:7:9 + | +LL | fn arr() { + | - help: try adding a return type: `-> [i32; 1]` +LL | [8; Self::LEN] + | ^^^^^^^^^^^^^^ expected `()`, found `[{integer}; 1]` + +error: aborting due to 2 previous errors; 2 warnings emitted + +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`.