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
64 changes: 58 additions & 6 deletions compiler/rustc_hir_analysis/src/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use std::debug_assert_matches;

use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{DelegationSelfTyPropagationKind, PathSegment};
Expand All @@ -21,6 +21,7 @@ type RemapTable = FxHashMap<u32, u32>;
struct ParamIndexRemapper<'tcx> {
tcx: TyCtxt<'tcx>,
remap_table: RemapTable,
delegation_parent_consts: FxHashSet<ty::ParamConst>,
}

impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ParamIndexRemapper<'tcx> {
Expand Down Expand Up @@ -337,7 +338,7 @@ fn create_generic_args<'tcx>(
delegation_id: LocalDefId,
mut parent_args: &[ty::GenericArg<'tcx>],
mut child_args: &[ty::GenericArg<'tcx>],
) -> Vec<ty::GenericArg<'tcx>> {
) -> (Vec<ty::GenericArg<'tcx>>, &'tcx [ty::GenericArg<'tcx>]) {
let delegation_generics = tcx.generics_of(delegation_id);
let delegation_args = ty::GenericArgs::identity_for_item(tcx, delegation_id);

Expand Down Expand Up @@ -389,7 +390,7 @@ fn create_generic_args<'tcx>(
let zero_self = zero_self.as_ref().into_iter();
let after_lifetimes_self = after_lifetimes_self.as_ref().into_iter();

zero_self
let args = zero_self
.chain(delegation_parent_args)
.chain(parent_args.iter().filter(|a| a.as_region().is_some()))
.chain(child_args.iter().filter(|a| a.as_region().is_some()))
Expand All @@ -398,7 +399,9 @@ fn create_generic_args<'tcx>(
.chain(child_args.iter().filter(|a| a.as_region().is_none()))
.chain(synth_args)
.copied()
.collect::<Vec<_>>()
.collect::<Vec<_>>();

(args, delegation_parent_args)
}

pub(crate) fn inherit_predicates_for_delegation_item<'tcx>(
Expand Down Expand Up @@ -434,6 +437,44 @@ pub(crate) fn inherit_predicates_for_delegation_item<'tcx>(
continue;
}

// If we have a constant in parent or child args that came from delegation

@BoxyUwU BoxyUwU Jul 8, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am quite lost reading this comment & generally with what's going on at a high level with delegation 😅

Why do we wind up "merging" the ParamEnv of Trait::foo with the ParamEnv of the inherent impl? Is this because you dont want to have people need to write something like: reuse foo::function::<T> where T: Bound?

View changes since the review

@aerooneqq aerooneqq Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When generating delegation's generic params we inherit predicates from delegation signature, so if we have:

trait Trait<T: Clone> {
  fn foo() { .. }
}

reuse Trait::foo;

// We will generate with inherited predicate: T: Clone
fn foo<Self, T>() {
  <Self as Trait<T>>:foo();
}

// If we specify generic args:
reuse Trait::<usize>::foo;
// We will generate with inherited predicate: usize: Clone (after mapping T into usize)
fn foo<Self>() {
  <Self as Trait<usize>>:foo();
}

It works fine with types, but with consts if we have two ConstArgHasType predicates in env it panics.

After looking at it again and reading your comment maybe we should not inherit predicates at all for specified generic args, so we will not inherit usize: Clone, I will experiment with it and rewrite the fix in case of success.

// parent:
// ```rust
// trait Trait<T, const B: bool> { /* .. */}
// impl<const N: usize> S<N> {
// reuse Trait::<S<N>, N>::foo;
// }
// ```
// Then if we inherit const predicate from `Trait` then we end up with
// two `ConstArgHasType` for `N` constant:
// 1) ConstArgHasType(N/#0, bool) from `Trait`
// 2) ConstArgHasType(N/#0, usize) from delegation parent
// So in case the constant came from delegation parent we will not inherit
// ConstArgHasType from signature.
// The check is so complicated because we build generic args for signature
// and predicates inheritance, for the example above it will be
// `args = [S<N/#0>, N/#0, S<N/#0>, N/#0]`, where
// args[0] - Self type, args[1] - delegation parent const, args[2] - first
// arg of callee path, args[3] - second arg of callee path.
// When processing predicate ConstArgHasType(B/#2, bool)
// from delegation signature (`Trait::foo`), we need to map `B/#2` into some
// arg from `args`. The mapping which is built by `create_mapping` function is:
// `{0: 0, 2: 3, 1: 2}`, so as `B/#2` has index `2` it is mapped into third
// arg from `args` - `N/#0`. After we obtained mapped const param, we check if
// it came from delegation parent, and if so we do not process its `ConstArgHasType`
// predicate.
// (Issue #158675).
if let ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) =
pred.0.as_predicate().fold_with(&mut self.folder).kind().skip_binder()
{
let unnorm_const = EarlyBinder::bind(self.tcx, ct).instantiate(self.tcx, args);
if let ty::ConstKind::Param(param) = unnorm_const.skip_norm_wip().kind()
&& self.folder.delegation_parent_consts.contains(&param)
{
continue;
}
}

let new_pred = pred.0.fold_with(&mut self.folder);
self.preds.push((
EarlyBinder::bind(self.tcx, new_pred)
Expand Down Expand Up @@ -497,10 +538,21 @@ fn create_folder_and_args<'tcx>(
parent_args: &'tcx [ty::GenericArg<'tcx>],
child_args: &'tcx [ty::GenericArg<'tcx>],
) -> (ParamIndexRemapper<'tcx>, Vec<ty::GenericArg<'tcx>>) {
let args = create_generic_args(tcx, sig_id, def_id, parent_args, child_args);
let (args, delegation_parent_args) =
create_generic_args(tcx, sig_id, def_id, parent_args, child_args);

let remap_table = create_mapping(tcx, sig_id, def_id);

(ParamIndexRemapper { tcx, remap_table }, args)
let delegation_parent_consts = delegation_parent_args
.iter()
.filter_map(|a| {
a.as_const().and_then(|c| {
if let ty::ConstKind::Param(param) = c.kind() { Some(param) } else { None }
})
})
.collect();

(ParamIndexRemapper { tcx, remap_table, delegation_parent_consts }, args)
}

fn check_constraints<'tcx>(
Expand Down
30 changes: 30 additions & 0 deletions tests/ui/delegation/generics/const-predicates-ice-158675.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#![feature(fn_delegation)]

mod original_ice {
struct S<const N: usize>;

trait Trait<T, const B: bool> {
fn fun();
}

impl<const N: usize> S<N> {
reuse Trait::<S<N>, N>::fun;
//~^ ERROR: the constant `N` is not of type `bool`
}
}

mod with_child_constants {
struct S<const N: usize>;

trait Trait<T, const B: bool> {
fn fun<const C: char>();
}

impl<const N: usize> S<N> {
reuse Trait::<S<N>, N>::fun::<N>;
//~^ ERROR: the constant `N` is not of type `bool`
//~| ERROR: the constant `N` is not of type `char`
}
}

fn main() {}
42 changes: 42 additions & 0 deletions tests/ui/delegation/generics/const-predicates-ice-158675.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
error: the constant `N` is not of type `bool`
--> $DIR/const-predicates-ice-158675.rs:11:29
|
LL | reuse Trait::<S<N>, N>::fun;
| ^ expected `bool`, found `usize`
|
note: required by a const generic parameter in `original_ice::Trait::fun`
--> $DIR/const-predicates-ice-158675.rs:6:20
|
LL | trait Trait<T, const B: bool> {
| ^^^^^^^^^^^^^ required by this const generic parameter in `Trait::fun`
LL | fn fun();
| --- required by a bound in this associated function

error: the constant `N` is not of type `bool`
--> $DIR/const-predicates-ice-158675.rs:24:29
|
LL | reuse Trait::<S<N>, N>::fun::<N>;
| ^ expected `bool`, found `usize`
|
note: required by a const generic parameter in `with_child_constants::Trait::fun`
--> $DIR/const-predicates-ice-158675.rs:19:20
|
LL | trait Trait<T, const B: bool> {
| ^^^^^^^^^^^^^ required by this const generic parameter in `Trait::fun`
LL | fn fun<const C: char>();
| --- required by a bound in this associated function

error: the constant `N` is not of type `char`
--> $DIR/const-predicates-ice-158675.rs:24:39
|
LL | reuse Trait::<S<N>, N>::fun::<N>;
| ^ expected `char`, found `usize`
|
note: required by a const generic parameter in `with_child_constants::Trait::fun`
--> $DIR/const-predicates-ice-158675.rs:20:16
|
LL | fn fun<const C: char>();
| ^^^^^^^^^^^^^ required by this const generic parameter in `Trait::fun`

error: aborting due to 3 previous errors

Loading