Skip to content

Rework Bounds collection #106235

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 8, 2023
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
93 changes: 65 additions & 28 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
ty::Binder::bind_with_vars(tcx.mk_trait_ref(trait_def_id, substs), bound_vars);

debug!(?poly_trait_ref, ?assoc_bindings);
bounds.trait_bounds.push((poly_trait_ref, span, constness));
bounds.push_trait_bound(tcx, poly_trait_ref, span, constness);

let mut dup_bindings = FxHashMap::default();
for binding in &assoc_bindings {
Expand Down Expand Up @@ -853,18 +853,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}

/// Sets `implicitly_sized` to true on `Bounds` if necessary
pub(crate) fn add_implicitly_sized<'hir>(
pub(crate) fn add_implicitly_sized(
&self,
bounds: &mut Bounds<'hir>,
ast_bounds: &'hir [hir::GenericBound<'hir>],
self_ty_where_predicates: Option<(LocalDefId, &'hir [hir::WherePredicate<'hir>])>,
bounds: &mut Bounds<'tcx>,
self_ty: Ty<'tcx>,
ast_bounds: &'tcx [hir::GenericBound<'tcx>],
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
span: Span,
) {
let tcx = self.tcx();

// Try to find an unbound in bounds.
let mut unbound = None;
let mut search_bounds = |ast_bounds: &'hir [hir::GenericBound<'hir>]| {
let mut search_bounds = |ast_bounds: &'tcx [hir::GenericBound<'tcx>]| {
for ab in ast_bounds {
if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab {
if unbound.is_none() {
Expand Down Expand Up @@ -912,7 +913,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// No lang item for `Sized`, so we can't add it as a bound.
return;
}
bounds.implicitly_sized = Some(span);
bounds.push_sized(tcx, self_ty, span);
}

/// This helper takes a *converted* parameter type (`param_ty`)
Expand Down Expand Up @@ -963,10 +964,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
hir::GenericBound::Outlives(lifetime) => {
let region = self.ast_region_to_region(lifetime, None);
bounds.region_bounds.push((
ty::Binder::bind_with_vars(region, bound_vars),
bounds.push_region_bound(
self.tcx(),
ty::Binder::bind_with_vars(
ty::OutlivesPredicate(param_ty, region),
bound_vars,
),
lifetime.ident.span,
));
);
}
}
}
Expand Down Expand Up @@ -1225,13 +1230,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
};
}
}
bounds.projection_bounds.push((
projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate {
projection_ty,
term: term,
}),
bounds.push_projection_bound(
tcx,
projection_ty
.map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }),
binding.span,
));
);
}
ConvertedBindingKind::Constraint(ast_bounds) => {
// "Desugar" a constraint like `T: Iterator<Item: Debug>` to
Expand Down Expand Up @@ -1260,7 +1264,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
fn conv_object_ty_poly_trait_ref(
&self,
span: Span,
trait_bounds: &[hir::PolyTraitRef<'_>],
hir_trait_bounds: &[hir::PolyTraitRef<'_>],
lifetime: &hir::Lifetime,
borrowed: bool,
representation: DynKind,
Expand All @@ -1270,7 +1274,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let mut bounds = Bounds::default();
let mut potential_assoc_types = Vec::new();
let dummy_self = self.tcx().types.trait_object_dummy_self;
for trait_bound in trait_bounds.iter().rev() {
for trait_bound in hir_trait_bounds.iter().rev() {
if let GenericArgCountResult {
correct:
Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
Expand All @@ -1287,10 +1291,45 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
}

let mut trait_bounds = vec![];
let mut projection_bounds = vec![];
for (pred, span) in bounds.predicates() {
let bound_pred = pred.kind();
match bound_pred.skip_binder() {
ty::PredicateKind::Clause(clause) => match clause {
ty::Clause::Trait(trait_pred) => {
assert_eq!(trait_pred.polarity, ty::ImplPolarity::Positive);
trait_bounds.push((
bound_pred.rebind(trait_pred.trait_ref),
span,
trait_pred.constness,
));
}
ty::Clause::Projection(proj) => {
projection_bounds.push((bound_pred.rebind(proj), span));
}
ty::Clause::TypeOutlives(_) => {
// Do nothing, we deal with regions separately
}
ty::Clause::RegionOutlives(_) => bug!(),
},
ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(_, _, _)
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::ConstEvaluatable(_)
| ty::PredicateKind::ConstEquate(_, _)
| ty::PredicateKind::TypeWellFormedFromEnv(_)
| ty::PredicateKind::Ambiguous => bug!(),
}
}

// Expand trait aliases recursively and check that only one regular (non-auto) trait
// is used and no 'maybe' bounds are used.
let expanded_traits =
traits::expand_trait_aliases(tcx, bounds.trait_bounds.iter().map(|&(a, b, _)| (a, b)));
traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b, _)| (a, b)));

let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits
.filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self)
.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
Expand Down Expand Up @@ -1327,8 +1366,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}

if regular_traits.is_empty() && auto_traits.is_empty() {
let trait_alias_span = bounds
.trait_bounds
let trait_alias_span = trait_bounds
.iter()
.map(|&(trait_ref, _, _)| trait_ref.def_id())
.find(|&trait_ref| tcx.is_trait_alias(trait_ref))
Expand Down Expand Up @@ -1359,8 +1397,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// Use a `BTreeSet` to keep output in a more consistent order.
let mut associated_types: FxHashMap<Span, BTreeSet<DefId>> = FxHashMap::default();

let regular_traits_refs_spans = bounds
.trait_bounds
let regular_traits_refs_spans = trait_bounds
.into_iter()
.filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id()));

Expand Down Expand Up @@ -1414,15 +1451,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// the discussion in #56288 for alternatives.
if !references_self {
// Include projections defined on supertraits.
bounds.projection_bounds.push((pred, span));
projection_bounds.push((pred, span));
}
}
_ => (),
}
}
}

for (projection_bound, _) in &bounds.projection_bounds {
for (projection_bound, _) in &projection_bounds {
for def_ids in associated_types.values_mut() {
def_ids.remove(&projection_bound.projection_def_id());
}
Expand All @@ -1431,7 +1468,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self.complain_about_missing_associated_types(
associated_types,
potential_assoc_types,
trait_bounds,
hir_trait_bounds,
);

// De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as
Expand Down Expand Up @@ -1473,7 +1510,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let substs = tcx.intern_substs(&substs[..]);

let span = i.bottom().1;
let empty_generic_args = trait_bounds.iter().any(|hir_bound| {
let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
&& hir_bound.span.contains(span)
});
Expand Down Expand Up @@ -1505,7 +1542,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
})
});

let existential_projections = bounds.projection_bounds.iter().map(|(bound, _)| {
let existential_projections = projection_bounds.iter().map(|(bound, _)| {
bound.map_bound(|mut b| {
assert_eq!(b.projection_ty.self_ty(), dummy_self);

Expand Down
93 changes: 37 additions & 56 deletions compiler/rustc_hir_analysis/src/bounds.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Bounds are restrictions applied to some types after they've been converted into the
//! `ty` form from the HIR.

use rustc_hir::LangItem;
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
use rustc_span::Span;

Expand All @@ -15,73 +16,53 @@ use rustc_span::Span;
/// ^^^^^^^^^ bounding the type parameter `T`
///
/// impl dyn Bar + Baz
/// ^^^^^^^^^ bounding the forgotten dynamic type
/// ^^^^^^^^^ bounding the type-erased dynamic type
/// ```
///
/// Our representation is a bit mixed here -- in some cases, we
/// include the self type (e.g., `trait_bounds`) but in others we do not
#[derive(Default, PartialEq, Eq, Clone, Debug)]
pub struct Bounds<'tcx> {
/// A list of region bounds on the (implicit) self type. So if you
/// had `T: 'a + 'b` this might would be a list `['a, 'b]` (but
/// the `T` is not explicitly included).
pub region_bounds: Vec<(ty::Binder<'tcx, ty::Region<'tcx>>, Span)>,

/// A list of trait bounds. So if you had `T: Debug` this would be
/// `T: Debug`. Note that the self-type is explicit here.
pub trait_bounds: Vec<(ty::PolyTraitRef<'tcx>, Span, ty::BoundConstness)>,

/// A list of projection equality bounds. So if you had `T:
/// Iterator<Item = u32>` this would include `<T as
/// Iterator>::Item => u32`. Note that the self-type is explicit
/// here.
pub projection_bounds: Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>,

/// `Some` if there is *no* `?Sized` predicate. The `span`
/// is the location in the source of the `T` declaration which can
/// be cited as the source of the `T: Sized` requirement.
pub implicitly_sized: Option<Span>,
pub predicates: Vec<(ty::Predicate<'tcx>, Span)>,
}

impl<'tcx> Bounds<'tcx> {
/// Converts a bounds list into a flat set of predicates (like
/// where-clauses). Because some of our bounds listings (e.g.,
/// regions) don't include the self-type, you must supply the
/// self-type here (the `param_ty` parameter).
pub fn predicates<'out, 's>(
&'s self,
pub fn push_region_bound(
&mut self,
tcx: TyCtxt<'tcx>,
param_ty: Ty<'tcx>,
// the output must live shorter than the duration of the borrow of self and 'tcx.
) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> + 'out
where
'tcx: 'out,
's: 'out,
{
// If it could be sized, and is, add the `Sized` predicate.
let sized_predicate = self.implicitly_sized.and_then(|span| {
// FIXME: use tcx.at(span).mk_trait_ref(LangItem::Sized) here? This may make no-core code harder to write.
let sized = tcx.lang_items().sized_trait()?;
let trait_ref = ty::Binder::dummy(tcx.mk_trait_ref(sized, [param_ty]));
Some((trait_ref.without_const().to_predicate(tcx), span))
});
region: ty::PolyTypeOutlivesPredicate<'tcx>,
span: Span,
) {
self.predicates.push((region.to_predicate(tcx), span));
}

let region_preds = self.region_bounds.iter().map(move |&(region_bound, span)| {
let pred = region_bound
.map_bound(|region_bound| ty::OutlivesPredicate(param_ty, region_bound))
.to_predicate(tcx);
(pred, span)
});
let trait_bounds =
self.trait_bounds.iter().map(move |&(bound_trait_ref, span, constness)| {
let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx);
(predicate, span)
});
let projection_bounds = self
.projection_bounds
.iter()
.map(move |&(projection, span)| (projection.to_predicate(tcx), span));
pub fn push_trait_bound(
&mut self,
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
span: Span,
constness: ty::BoundConstness,
) {
self.predicates.push((trait_ref.with_constness(constness).to_predicate(tcx), span));
}

pub fn push_projection_bound(
&mut self,
tcx: TyCtxt<'tcx>,
projection: ty::PolyProjectionPredicate<'tcx>,
span: Span,
) {
self.predicates.push((projection.to_predicate(tcx), span));
}

pub fn push_sized(&mut self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) {
let sized_def_id = tcx.require_lang_item(LangItem::Sized, Some(span));
let trait_ref = ty::Binder::dummy(tcx.mk_trait_ref(sized_def_id, [ty]));
// Preferrable to put this obligation first, since we report better errors for sized ambiguity.
self.predicates.insert(0, (trait_ref.without_const().to_predicate(tcx), span));
}

sized_predicate.into_iter().chain(region_preds).chain(trait_bounds).chain(projection_bounds)
pub fn predicates(&self) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> + '_ {
self.predicates.iter().cloned()
}
}
10 changes: 4 additions & 6 deletions compiler/rustc_hir_analysis/src/collect/item_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn associated_type_bounds<'tcx>(
let icx = ItemCtxt::new(tcx, assoc_item_def_id);
let mut bounds = <dyn AstConv<'_>>::compute_bounds(&icx, item_ty, ast_bounds);
// Associated types are implicitly sized unless a `?Sized` bound is found
<dyn AstConv<'_>>::add_implicitly_sized(&icx, &mut bounds, ast_bounds, None, span);
<dyn AstConv<'_>>::add_implicitly_sized(&icx, &mut bounds, item_ty, ast_bounds, None, span);

let trait_def_id = tcx.parent(assoc_item_def_id);
let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id.expect_local());
Expand All @@ -44,9 +44,7 @@ fn associated_type_bounds<'tcx>(
}
});

let all_bounds = tcx
.arena
.alloc_from_iter(bounds.predicates(tcx, item_ty).into_iter().chain(bounds_from_parent));
let all_bounds = tcx.arena.alloc_from_iter(bounds.predicates().chain(bounds_from_parent));
debug!("associated_type_bounds({}) = {:?}", tcx.def_path_str(assoc_item_def_id), all_bounds);
all_bounds
}
Expand Down Expand Up @@ -74,10 +72,10 @@ fn opaque_type_bounds<'tcx>(
let icx = ItemCtxt::new(tcx, opaque_def_id);
let mut bounds = <dyn AstConv<'_>>::compute_bounds(&icx, item_ty, ast_bounds);
// Opaque types are implicitly sized unless a `?Sized` bound is found
<dyn AstConv<'_>>::add_implicitly_sized(&icx, &mut bounds, ast_bounds, None, span);
<dyn AstConv<'_>>::add_implicitly_sized(&icx, &mut bounds, item_ty, ast_bounds, None, span);
debug!(?bounds);

tcx.arena.alloc_from_iter(bounds.predicates(tcx, item_ty))
tcx.arena.alloc_from_iter(bounds.predicates())
})
}

Expand Down
Loading