Auto merge of #102254 - matthiaskrgr:rollup-gitu6li, r=matthiaskrgr

Rollup of 5 pull requests

Successful merges:

 - #102016 (implied_bounds: deal with inference vars)
 - #102161 (Resolve async fn signature even without body (e.g., in trait))
 - #102216 (rustdoc: Stabilize --diagnostic-width)
 - #102240 (rustdoc: remove unused CSS `#main-content > .line-numbers`)
 - #102242 (rustdoc: remove unused CSS `.summary`)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2022-09-25 09:33:57 +00:00
commit 6f6010b08b
11 changed files with 187 additions and 80 deletions

View File

@ -53,6 +53,7 @@ pub struct OutlivesEnvironment<'tcx> {
}
/// Builder of OutlivesEnvironment.
#[derive(Debug)]
struct OutlivesEnvironmentBuilder<'tcx> {
param_env: ty::ParamEnv<'tcx>,
region_relation: TransitiveRelationBuilder<Region<'tcx>>,
@ -109,6 +110,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
impl<'a, 'tcx> OutlivesEnvironmentBuilder<'tcx> {
#[inline]
#[instrument(level = "debug")]
fn build(self) -> OutlivesEnvironment<'tcx> {
OutlivesEnvironment {
param_env: self.param_env,

View File

@ -805,7 +805,12 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
sig.decl.has_self(),
sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)),
&sig.decl.output,
)
);
this.record_lifetime_params_for_async(
fn_id,
sig.header.asyncness.opt_return_id(),
);
},
);
return;
@ -847,41 +852,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
},
);
// Construct the list of in-scope lifetime parameters for async lowering.
// We include all lifetime parameters, either named or "Fresh".
// The order of those parameters does not matter, as long as it is
// deterministic.
if let Some((async_node_id, _)) = async_node_id {
let mut extra_lifetime_params = this
.r
.extra_lifetime_params_map
.get(&fn_id)
.cloned()
.unwrap_or_default();
for rib in this.lifetime_ribs.iter().rev() {
extra_lifetime_params.extend(
rib.bindings
.iter()
.map(|(&ident, &(node_id, res))| (ident, node_id, res)),
);
match rib.kind {
LifetimeRibKind::Item => break,
LifetimeRibKind::AnonymousCreateParameter {
binder, ..
} => {
if let Some(earlier_fresh) =
this.r.extra_lifetime_params_map.get(&binder)
{
extra_lifetime_params.extend(earlier_fresh);
}
}
_ => {}
}
}
this.r
.extra_lifetime_params_map
.insert(async_node_id, extra_lifetime_params);
}
this.record_lifetime_params_for_async(fn_id, async_node_id);
if let Some(body) = body {
// Ignore errors in function bodies if this is rustdoc
@ -3926,6 +3897,36 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
Some((ident.name, ns)),
)
}
/// Construct the list of in-scope lifetime parameters for async lowering.
/// We include all lifetime parameters, either named or "Fresh".
/// The order of those parameters does not matter, as long as it is
/// deterministic.
fn record_lifetime_params_for_async(
&mut self,
fn_id: NodeId,
async_node_id: Option<(NodeId, Span)>,
) {
if let Some((async_node_id, _)) = async_node_id {
let mut extra_lifetime_params =
self.r.extra_lifetime_params_map.get(&fn_id).cloned().unwrap_or_default();
for rib in self.lifetime_ribs.iter().rev() {
extra_lifetime_params.extend(
rib.bindings.iter().map(|(&ident, &(node_id, res))| (ident, node_id, res)),
);
match rib.kind {
LifetimeRibKind::Item => break,
LifetimeRibKind::AnonymousCreateParameter { binder, .. } => {
if let Some(earlier_fresh) = self.r.extra_lifetime_params_map.get(&binder) {
extra_lifetime_params.extend(earlier_fresh);
}
}
_ => {}
}
}
self.r.extra_lifetime_params_map.insert(async_node_id, extra_lifetime_params);
}
}
}
struct LifetimeCountVisitor<'a, 'b> {

View File

@ -46,7 +46,7 @@ impl<'a, 'cx, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'cx, 'tcx> {
/// Note that this may cause outlives obligations to be injected
/// into the inference context with this body-id.
/// - `ty`, the type that we are supposed to assume is WF.
#[instrument(level = "debug", skip(self, param_env, body_id))]
#[instrument(level = "debug", skip(self, param_env, body_id), ret)]
fn implied_outlives_bounds(
&self,
param_env: ty::ParamEnv<'tcx>,
@ -71,6 +71,7 @@ impl<'a, 'cx, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'cx, 'tcx> {
let TypeOpOutput { output, constraints, .. } = result;
if let Some(constraints) = constraints {
debug!(?constraints);
// Instantiation may have produced new inference variables and constraints on those
// variables. Process these constraints.
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.tcx);

View File

@ -49,7 +49,8 @@ fn compute_implied_outlives_bounds<'tcx>(
let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
let mut wf_args = vec![ty.into()];
let mut implied_bounds = vec![];
let mut outlives_bounds: Vec<ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>> =
vec![];
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx);
@ -65,30 +66,17 @@ fn compute_implied_outlives_bounds<'tcx>(
// than the ultimate set. (Note: normally there won't be
// unresolved inference variables here anyway, but there might be
// during typeck under some circumstances.)
//
// FIXME(@lcnr): It's not really "always fine", having fewer implied
// bounds can be backward incompatible, e.g. #101951 was caused by
// us not dealing with inference vars in `TypeOutlives` predicates.
let obligations = wf::obligations(infcx, param_env, hir::CRATE_HIR_ID, 0, arg, DUMMY_SP)
.unwrap_or_default();
// N.B., all of these predicates *ought* to be easily proven
// true. In fact, their correctness is (mostly) implied by
// other parts of the program. However, in #42552, we had
// an annoying scenario where:
//
// - Some `T::Foo` gets normalized, resulting in a
// variable `_1` and a `T: Trait<Foo=_1>` constraint
// (not sure why it couldn't immediately get
// solved). This result of `_1` got cached.
// - These obligations were dropped on the floor here,
// rather than being registered.
// - Then later we would get a request to normalize
// `T::Foo` which would result in `_1` being used from
// the cache, but hence without the `T: Trait<Foo=_1>`
// constraint. As a result, `_1` never gets resolved,
// and we get an ICE (in dropck).
//
// Therefore, we register any predicates involving
// inference variables. We restrict ourselves to those
// involving inference variables both for efficiency and
// to avoids duplicate errors that otherwise show up.
// While these predicates should all be implied by other parts of
// the program, they are still relevant as they may constrain
// inference variables, which is necessary to add the correct
// implied bounds in some cases, mostly when dealing with projections.
fulfill_cx.register_predicate_obligations(
infcx,
obligations.iter().filter(|o| o.predicate.has_infer_types_or_consts()).cloned(),
@ -96,10 +84,10 @@ fn compute_implied_outlives_bounds<'tcx>(
// From the full set of obligations, just filter down to the
// region relationships.
implied_bounds.extend(obligations.into_iter().flat_map(|obligation| {
outlives_bounds.extend(obligations.into_iter().filter_map(|obligation| {
assert!(!obligation.has_escaping_bound_vars());
match obligation.predicate.kind().no_bound_vars() {
None => vec![],
None => None,
Some(pred) => match pred {
ty::PredicateKind::Trait(..)
| ty::PredicateKind::Subtype(..)
@ -109,21 +97,18 @@ fn compute_implied_outlives_bounds<'tcx>(
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => vec![],
| ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
ty::PredicateKind::WellFormed(arg) => {
wf_args.push(arg);
vec![]
None
}
ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => {
vec![OutlivesBound::RegionSubRegion(r_b, r_a)]
Some(ty::OutlivesPredicate(r_a.into(), r_b))
}
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, r_b)) => {
let ty_a = infcx.resolve_vars_if_possible(ty_a);
let mut components = smallvec![];
push_outlives_components(tcx, ty_a, &mut components);
implied_bounds_from_components(r_b, components)
Some(ty::OutlivesPredicate(ty_a.into(), r_b))
}
},
}
@ -133,9 +118,27 @@ fn compute_implied_outlives_bounds<'tcx>(
// Ensure that those obligations that we had to solve
// get solved *here*.
match fulfill_cx.select_all_or_error(infcx).as_slice() {
[] => Ok(implied_bounds),
_ => Err(NoSolution),
[] => (),
_ => return Err(NoSolution),
}
// We lazily compute the outlives components as
// `select_all_or_error` constrains inference variables.
let implied_bounds = outlives_bounds
.into_iter()
.flat_map(|ty::OutlivesPredicate(a, r_b)| match a.unpack() {
ty::GenericArgKind::Lifetime(r_a) => vec![OutlivesBound::RegionSubRegion(r_b, r_a)],
ty::GenericArgKind::Type(ty_a) => {
let ty_a = infcx.resolve_vars_if_possible(ty_a);
let mut components = smallvec![];
push_outlives_components(tcx, ty_a, &mut components);
implied_bounds_from_components(r_b, components)
}
ty::GenericArgKind::Const(_) => unreachable!(),
})
.collect();
Ok(implied_bounds)
}
/// When we have an implied bound that `T: 'a`, we can further break

View File

@ -140,6 +140,7 @@ pub(crate) fn compare_impl_method<'tcx>(
///
/// Finally we register each of these predicates as an obligation and check that
/// they hold.
#[instrument(level = "debug", skip(tcx, impl_m_span, impl_trait_ref))]
fn compare_predicate_entailment<'tcx>(
tcx: TyCtxt<'tcx>,
impl_m: &AssocItem,

View File

@ -1130,10 +1130,6 @@ so that we can apply CSS-filters to change the arrow color in themes */
font-size: 1rem;
}
.summary {
padding-right: 0px;
}
pre.rust .question-mark {
font-weight: bold;
}
@ -1917,10 +1913,6 @@ in storage.js plus the media query with (min-width: 701px)
border-bottom: 1px solid;
}
#main-content > .line-numbers {
margin-top: 0;
}
.notable-traits .notable-traits-tooltiptext {
left: 0;
top: 100%;

View File

@ -461,7 +461,7 @@ fn opts() -> Vec<RustcOptGroup> {
"human|json|short",
)
}),
unstable("diagnostic-width", |o| {
stable("diagnostic-width", |o| {
o.optopt(
"",
"diagnostic-width",

View File

@ -1,4 +1,4 @@
// compile-flags: -Zunstable-options --diagnostic-width=10
// compile-flags: --diagnostic-width=10
#![deny(rustdoc::bare_urls)]
/// This is a long line that contains a http://link.com

View File

@ -0,0 +1,46 @@
// check-pass
// edition:2021
#![feature(async_fn_in_trait)]
#![allow(incomplete_features)]
use std::future::Future;
async fn yield_now() {}
trait AsyncIterator {
type Item;
async fn next(&mut self) -> Option<Self::Item>;
}
struct YieldingRange {
counter: u32,
stop: u32,
}
impl AsyncIterator for YieldingRange {
type Item = u32;
async fn next(&mut self) -> Option<Self::Item> {
if self.counter == self.stop {
None
} else {
let c = self.counter;
self.counter += 1;
yield_now().await;
Some(c)
}
}
}
async fn async_main() {
let mut x = YieldingRange { counter: 0, stop: 10 };
while let Some(v) = x.next().await {
println!("Hi: {v}");
}
}
fn main() {
let _ = async_main();
}

View File

@ -0,0 +1,50 @@
// Taken directly from that issue.
//
// This test detected that we didn't correctly resolve
// inference variables when computing implied bounds.
//
// check-pass
pub trait BuilderFn<'a> {
type Output;
}
impl<'a, F, Out> BuilderFn<'a> for F
where
F: FnOnce(&'a mut ()) -> Out,
{
type Output = Out;
}
pub trait ConstructionFirm {
type Builder: for<'a> BuilderFn<'a>;
}
pub trait Campus<T>
where
T: ConstructionFirm,
{
fn add_building(
&mut self,
building: &mut <<T as ConstructionFirm>::Builder as BuilderFn<'_>>::Output,
);
}
struct ArchitectsInc {}
impl ConstructionFirm for ArchitectsInc {
type Builder = fn(&mut ()) -> PrettyCondo<'_>;
}
struct PrettyCondo<'a> {
_marker: &'a mut (),
}
struct CondoEstate {}
impl Campus<ArchitectsInc> for CondoEstate {
fn add_building(&mut self, _building: &mut PrettyCondo<'_>) {
todo!()
}
}
fn main() {}

View File

@ -0,0 +1,11 @@
// check-pass
// This is currently stable behavior, which was almost accidentally made an
// error in #102161 since there is no test exercising it. I am not sure if
// this _should_ be the desired behavior, but at least we should know if it
// changes.
fn main() {}
trait Foo {
fn fn_with_type_named_same_as_local_in_param(b: i32, b: i32);
}