Code change:
Instead, this happened: Successful compilation Tested on the playground, all versions and editions. Stable channel: 1.64.0 Beta channel: 1.65.0-beta.3 (2022-10-10 ) Nightly channel: 1.66.0-nightly (2022-10-19 ) I'm labeling this as a regression as I believe it is severe enough of an unplanned change (at best an accidental stabilization?) that I should draw attention to it, even though I haven't found a way to cause miscompilation for example. I also have not tried to do so at all yet. label +T-compiler +A-lifetimes +regression-from-stable-to-stable <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
searched nightlies: from nightly-2022-01-01 to nightly-2022-10-01 regressed nightly: nightly-2022-07-26 searched commit range: regressed commit: <details<summarybisected with <a href=''cargo-bisect-rustc</av0.6.4</summaryHost triple: x86_64-unknown-linux-gnu Reproduce with: </details
claim
Actually, this behavior is correct as : There is exactly one lifetime used in the parameters (), so assigning it to the output lifetime is as expected. release-assignment
That verbiage but it compiles on no stable version of rustc prior to 1.64, so I don't think it's actually relevant. (The reference is also explicitly non-normative.) mirrors in specifying input positions, not counts. Regardless of documentation, as something that has not compiled on Rust 1.0--1.63 but compiles today, this is clearly a change in language behavior without FCP which I believe to be unintentional. label +T-lang
Ok, so we will consider this for now a regression that should be backported to stable (we're ~2 weeks away from the new release). WG-prioritization assigning priority (). label -I-prioritize +P-critical
Filed
Nominating for lang team to figure out what we should do with this. Will also ping on zulip.
Discussed in lang, dropping nomination. Agreement that we want to revert the change in behavior and backport that to 1.65 (and 1.64, but that's not happening given short time window). Considering whether we want to make the behavior change can happen in a follow-up proposal.

Related discussion:
text: } else { None }, <del> lifetime_count: local_count, </del> <ins> lifetime_count, </ins> span: ty.span, }); <ins> all_candidates.extend(candidates.into_iter().filter_map(|(_, candidate)| { match candidate { LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => { None } LifetimeElisionCandidate::Missing(missing) => Some(missing), } })); } let mut distinct_iter = distinct.into_iter(); if let Some(res) = distinct_iter.next() { match elision_lifetime { // We are the first parameter to bind lifetimes. Elision::None => { if distinct_iter.next().is_none() { // We have a single lifetime => success. elision_lifetime = Elision::Param(res) } else { // We have have multiple lifetimes => error. elision_lifetime = Elision::Err; } } // We have 2 parameters that bind lifetimes => error. Elision::Param(_) => elision_lifetime = Elision::Err, // `self` elision takes precedence over everything else. Elision::Self_(_) | Elision::Err => {} } </ins> } <del> lifetime_count = new_count; </del> } // Handle `self` specially. if index == 0 && has_self { let self_lifetime = self.find_lifetime_for_self(ty); if let Set1::One(lifetime) = self_lifetime { <del> elision_lifetime = Some(lifetime); self.lifetime_elision_candidates = None; </del> <ins> // We found `self` elision. elision_lifetime = Elision::Self_(lifetime); </ins> } else { <del> self.lifetime_elision_candidates = Some(Default::default()); lifetime_count = 0; </del> <ins> // We do not have `self` elision: disregard the `Elision::Param` that we may // have found. elision_lifetime = Elision::None; </ins> } } debug!("(resolving function / closure) recorded parameter"); } <del> let all_candidates = replace(&mut self.lifetime_elision_candidates, outer_candidates); debug!(?all_candidates); </del> <ins> // Reinstate elision state. debug_assert_matches!(self.lifetime_elision_candidates, None); self.lifetime_elision_candidates = outer_candidates; </ins> <del> if let Some(res) = elision_lifetime { </del> <ins> if let Elision::Param(res) | Elision::Self_(res) = elision_lifetime { </ins> return Ok(res); } <del> // We do not have a `self` candidate, look at the full list. let all_candidates = all_candidates.unwrap(); if all_candidates.len() == 1 { Ok(*all_candidates.first().unwrap().0) } else { let all_candidates = all_candidates .into_iter() .filter_map(|(_, candidate)| match candidate { LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => None, LifetimeElisionCandidate::Missing(missing) => Some(missing), }) .collect(); Err((all_candidates, parameter_info)) } </del> <ins> // We do not have a candidate. Err((all_candidates, parameter_info)) </ins> } /// List all the lifetimes that appear in the provided type.