Change:
() As you can see, calling directly doesn't instantiate the HRTB (even though it needs to AFAIK) or if it does it doesn't normalize the resulting bound, whereas going through normalizes the instantiated argument types of , while 's definition can be type-checked as-is. EDIT: you can also get an ICE by uncommenting the use in : cc (this might be a duplicate)
Duplicate of (at least the ICE case)? A lot of analysis wad did in the other issue. Maybe it would help. I doubt that, in such cases the associate type do normalize at all. My assumption is: In the case that you marked as "working", it is just delayed the normalization to the later stage. So if the later stage consider this is dead code it is passed and compile, otherwise, when it really need to normalize it, run into ICE. So when called explicitly like , the compiler have no choice but to normalize with . If you instead though, the compiler checked that the requirement to match the argument can in theory fullfill, but the lifetime parameter was not free in the function body so it can consider this is a late bound. So the normalization is not happen right away. Later on, when you add the code in to call , the compiler have to do normalization again, but it is already in codegen (see the backtrace messages) phase, so normalization is unexpected and ICE occurs. Given all that, I guess a proper solution is to add a "late normalization" pass to normalize any postponed normalization like this.
This is a probably a duplicate of and/or ?
closing as duplicate of , which is where I will try to track future instances of this field of ICE.

Discussion:
/// N.B., while `f` is running, the thread-local state /// is `BridgeState::InUse`. fn with<R>(f: impl FnOnce(&mut BridgeState<'_>) -> R) -> R { <del> BRIDGE_STATE.with(|state| { state.replace(BridgeState::InUse, |mut state| { // FIXME(#52812) pass `f` directly to `replace` when `RefMutL` is gone f(&mut *state) }) }) </del> <ins> BRIDGE_STATE.with(|state| state.replace(BridgeState::InUse, f)) </ins> } }

use std::cell::Cell; use std::mem; <del> use std::ops::{Deref, DerefMut}; </del> /// Type lambda application, with a lifetime. #[allow(unused_lifetimes)]

impl<T: for<'a> ApplyL<'a>> LambdaL for T {} <del> // HACK(eddyb) work around projection limitations with a newtype // FIXME(#52812) replace with `&'a mut <T as ApplyL<'b>>::Out` pub struct RefMutL<'a, 'b, T: LambdaL>(&'a mut <T as ApplyL<'b>>::Out); impl<'a, 'b, T: LambdaL> Deref for RefMutL<'a, 'b, T> { type Target = <T as ApplyL<'b>>::Out; fn deref(&self) -> &Self::Target { self.0 } } impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> { fn deref_mut(&mut self) -> &mut Self::Target { self.0 } } </del> pub struct ScopedCell<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>); impl<T: LambdaL> ScopedCell<T> {

pub fn replace<'a, R>( &self, replacement: <T as ApplyL<'a>>::Out, <del> f: impl for<'b, 'c> FnOnce(RefMutL<'b, 'c, T>) -> R, </del> <ins> f: impl for<'b, 'c> FnOnce(&'b mut <T as ApplyL<'c>>::Out) -> R, </ins> ) -> R { /// Wrapper that ensures that the cell always gets filled /// (with the original state, optionally changed by `f`),

})), }; <del> f(RefMutL(put_back_on_drop.value.as_mut().unwrap())) </del> <ins> f(put_back_on_drop.value.as_mut().unwrap()) </ins> } /// Sets the value in `self` to `value` while running `f`.