Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
47 changes: 46 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
24 changes: 15 additions & 9 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
Expand Down
16 changes: 0 additions & 16 deletions tests/crashes/138089.rs

This file was deleted.

13 changes: 0 additions & 13 deletions tests/crashes/138226-2.rs

This file was deleted.

13 changes: 0 additions & 13 deletions tests/crashes/138226.rs

This file was deleted.

11 changes: 0 additions & 11 deletions tests/crashes/150960.rs

This file was deleted.

20 changes: 20 additions & 0 deletions tests/ui/associated-consts/type-const-in-array-len-wrong-type.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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

41 changes: 41 additions & 0 deletions tests/ui/associated-consts/type-const-in-array-len.rs
Original file line number Diff line number Diff line change
@@ -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>(A, B);
impl<A, B> Foo<A, B> {
#[type_const]
const LEN: usize = 4;

fn foo() {
let _ = [5; Self::LEN];
}
}

// Test case from #138226: generic impl with const parameter
struct Bar<const N: usize>;
impl<const N: usize> Bar<N> {
#[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() {}
19 changes: 19 additions & 0 deletions tests/ui/associated-consts/type-const-in-array-len.stderr
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/8995> for more information

warning: 2 warnings emitted

16 changes: 16 additions & 0 deletions tests/ui/associated-types/type-const-inherent-impl-normalize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
struct S<const N: usize>;
impl<const N: usize> S<N> {
#[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() {}
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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`.
Loading