Skip to content
Draft
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
154 changes: 152 additions & 2 deletions compiler/rustc_hir_analysis/src/collect/predicates_of.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::assert_matches::assert_matches;

use hir::Node;
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_hir as hir;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::find_attr;
use rustc_middle::ty::{
self, GenericPredicates, ImplTraitInTraitData, Ty, TyCtxt, TypeVisitable, TypeVisitor, Upcast,
self, Binder, Clause, GenericArgKind, GenericArgs, GenericPredicates, ImplTraitInTraitData, Ty,
TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitable, TypeVisitor, Upcast, UpcastFrom,
};
use rustc_middle::{bug, span_bug};
use rustc_span::{DUMMY_SP, Ident, Span};
Expand All @@ -31,6 +32,11 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
let mut result = tcx.explicit_predicates_of(def_id);
debug!("predicates_of: explicit_predicates_of({:?}) = {:?}", def_id, result);

let implied_item_bounds = elaborate_projection_predicates(tcx, result.predicates.to_vec());
result.predicates = tcx
.arena
.alloc_from_iter(result.predicates.into_iter().copied().chain(implied_item_bounds));

let inferred_outlives = tcx.inferred_outlives_of(def_id);
if !inferred_outlives.is_empty() {
debug!("predicates_of: inferred_outlives_of({:?}) = {:?}", def_id, inferred_outlives,);
Expand Down Expand Up @@ -77,6 +83,150 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
debug!("predicates_of({:?}) = {:?}", def_id, result);
result
}
// If the term of projection is a generic param, we try to add implied item bounds to predicates.
// FIXME: nested binders and the case that item bound contains bound vars are not handled.
fn elaborate_projection_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
predicates: Vec<(Clause<'tcx>, Span)>,
) -> Vec<(ty::Clause<'tcx>, Span)> {
struct PredicateArgFolder<'tcx> {
tcx: TyCtxt<'tcx>,
ty_mapping: FxHashMap<Ty<'tcx>, Ty<'tcx>>,
region_mapping: FxHashMap<ty::Region<'tcx>, ty::Region<'tcx>>,
const_mapping: FxHashMap<ty::Const<'tcx>, ty::Const<'tcx>>,
}
impl<'tcx> PredicateArgFolder<'tcx> {
fn new(tcx: TyCtxt<'tcx>, projection: ty::ProjectionPredicate<'tcx>) -> Self {
let mut ty_mapping = FxHashMap::default();
let mut region_mapping = FxHashMap::default();
let mut const_mapping = FxHashMap::default();
let assoc_ty = Ty::new_alias(
tcx,
ty::AliasTyKind::Projection,
ty::AliasTy::new(
tcx,
projection.projection_term.def_id,
GenericArgs::identity_for_item(tcx, projection.projection_term.def_id),
),
);
ty_mapping.insert(assoc_ty, projection.term.expect_type());
let target_assoc_args =
GenericArgs::identity_for_item(tcx, projection.projection_term.def_id);
for (target_arg, proj_arg) in
target_assoc_args.into_iter().zip(projection.projection_term.args)
{
match (target_arg.kind(), proj_arg.kind()) {
(GenericArgKind::Lifetime(r1), GenericArgKind::Lifetime(r2)) => {
region_mapping.insert(r1, r2);
}
(GenericArgKind::Type(t1), GenericArgKind::Type(t2)) => {
ty_mapping.insert(t1, t2);
}
(GenericArgKind::Const(c1), GenericArgKind::Const(c2)) => {
const_mapping.insert(c1, c2);
}
_ => bug!("mismatched generic arg kinds in projection predicate"),
}
}
debug!(
"elaborate_projection_predicates: ty_mapping = {:?}, region_mapping = {:?}, const_mapping = {:?}",
ty_mapping, region_mapping, const_mapping
);
Self { tcx, ty_mapping, region_mapping, const_mapping }
}
}
impl<'tcx> ty::TypeFolder<TyCtxt<'tcx>> for PredicateArgFolder<'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.tcx
}

fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
if let Some(replacement) = self.ty_mapping.get(&t) {
*replacement
} else {
t.super_fold_with(self)
}
}

fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
if let Some(replacement) = self.region_mapping.get(&r) { *replacement } else { r }
}

fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
if let Some(replacement) = self.const_mapping.get(&c) { *replacement } else { c }
}
}

let mut new_preds = Vec::new();
for (pred, _span) in &predicates {
if let ty::ClauseKind::Projection(proj_pred) = pred.kind().skip_binder()
&& let Some(proj_ty) = proj_pred.term.as_type()
{
// We should minimize this to allow the where clause check to be useful.
fn should_add_clause(t: Ty<'_>) -> bool {
match t.kind() {
ty::Param(_) => true,
ty::Alias(ty::Projection, alias) => alias.args.types().any(should_add_clause),
_ => false,
}
}
if !should_add_clause(proj_ty) {
continue;
}
debug!("elaborate_projection_predicates: projection predicate = {:?}", pred);
let assoc_bounds = tcx.explicit_item_bounds(proj_pred.projection_term.def_id);
debug!("elaborate_projection_predicates: original assoc_bounds = {:?}", assoc_bounds);
let mut folder = PredicateArgFolder::new(tcx, proj_pred);
// FIXME: optimize allocation later.
let assoc_bounds: Vec<_> = assoc_bounds
.skip_binder()
.iter()
.map(|(c, span)| {
(
Binder::bind_with_vars(
c.kind().skip_binder().fold_with(&mut folder),
pred.kind().bound_vars(),
),
*span,
)
})
.filter(|(c, _)| {
if let ty::ClauseKind::Projection(p) = c.skip_binder() {
if p.projection_term.to_term(tcx) == p.term {
// No need to add identity projection.
// They cause cycles later.
return false;
}
// We shouldn't add opposite projection of existing ones.
// They will be normalized to identity projection by each other.
// We also filter out projections which have the same lhs with existing
// projections.
// They cause ambiguity in normalization.
if predicates.iter().any(|(existing_c, _)| {
if let ty::ClauseKind::Projection(existing_p) =
existing_c.kind().skip_binder()
{
return p.projection_term == existing_p.projection_term
|| (existing_p.projection_term.to_term(tcx) == p.term
&& existing_p.term == p.projection_term.to_term(tcx));
}
false
}) {
return false;
}
}
true
})
.collect();
debug!("elaborate_projection_predicates: replaced assoc_bounds = {:?}", assoc_bounds);
let assoc_bounds: Vec<_> =
assoc_bounds.into_iter().map(|(c, s)| (Clause::upcast_from(c, tcx), s)).collect();
debug!("elaborate_projection_predicates: upcasted assoc_bounds = {:?}", assoc_bounds);
new_preds.extend(assoc_bounds);
}
}
new_preds
}

/// Returns a list of user-specified type predicates for the definition with ID `def_id`.
/// N.B., this does not include any implied/inferred constraints.
Expand Down
48 changes: 46 additions & 2 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,7 @@ pub fn clause_obligations<'tcx>(
wf.add_wf_preds_for_term(ty.into());
}
ty::ClauseKind::Projection(t) => {
wf.add_wf_preds_for_alias_term(t.projection_term);
wf.add_wf_preds_for_term(t.term);
wf.add_wf_preds_for_projection_pred(t);
}
ty::ClauseKind::ConstArgHasType(ct, ty) => {
wf.add_wf_preds_for_term(ct.into());
Expand Down Expand Up @@ -455,6 +454,50 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
}
}

fn add_wf_preds_for_projection_pred(&mut self, projection_pred: ty::ProjectionPredicate<'tcx>) {
let tcx = self.tcx();
let cause = self.cause(ObligationCauseCode::WellFormed(None));
debug!("add_wf_preds_for_projection_pred {:?}", projection_pred);

fn is_total_generic(t: Ty<'_>) -> bool {
match t.kind() {
ty::Param(_) => true,
ty::Alias(ty::Projection, alias) => alias.args.types().all(is_total_generic),
_ => false,
}
}
// Add item bounds to the predicate term.
// If the term is generic, we can skip these item bounds as they're implied by `AliasBound`.
if let Some(normalizes_to_ty) = projection_pred.term.as_type()
&& !is_total_generic(normalizes_to_ty)
{
let bounds = tcx.item_bounds(projection_pred.def_id());
debug!("add_wf_preds_for_projection_pred item_bounds={:?}", bounds);
let bound_obligations: Vec<_> = bounds
.instantiate(tcx, projection_pred.projection_term.args)
.iter()
.filter_map(|bound| {
if !bound.has_escaping_bound_vars() {
Some(traits::Obligation::with_depth(
tcx,
cause.clone(),
self.recursion_depth,
self.param_env,
bound,
))
} else {
None
}
})
.collect();
debug!("add_wf_preds_for_projection_pred bound_obligations={:?}", bound_obligations);
self.out.extend(bound_obligations);
}

self.add_wf_preds_for_alias_term(projection_pred.projection_term);
self.add_wf_preds_for_term(projection_pred.term);
}

/// Pushes the obligations required for an alias (except inherent) to be WF
/// into `self.out`.
fn add_wf_preds_for_alias_term(&mut self, data: ty::AliasTerm<'tcx>) {
Expand All @@ -480,6 +523,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// `i32: Copy`
// ]
let obligations = self.nominal_obligations(data.def_id, data.args);
debug!("add_wf_preds_for_alias_term nominal_obligations={:?}", obligations);
self.out.extend(obligations);

self.add_wf_preds_for_projection_args(data.args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ trait SuperTrait {

fn take0(_: impl Trait<N = 0, N = ()>) {}

fn take1(_: impl Trait<Q = "...", Q = [()]>) {}
fn take1(_: impl Trait<Q = "...", Q = ()>) {}

fn main() {}
2 changes: 2 additions & 0 deletions tests/ui/associated-type-bounds/issue-102335-ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ trait T {
type A: S<C<i32 = u32> = ()>; // Just one erroneous equality constraint
//~^ ERROR associated item constraints are not allowed here
//~| ERROR associated item constraints are not allowed here
//~| ERROR: the trait bound `(): Q` is not satisfied [E0277]
}

trait T2 {
type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equality constraints
//~^ ERROR associated item constraints are not allowed here
//~| ERROR associated item constraints are not allowed here
//~| ERROR: the trait bound `(): Q` is not satisfied [E0277]
}

trait Q {}
Expand Down
33 changes: 29 additions & 4 deletions tests/ui/associated-type-bounds/issue-102335-ty.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,20 @@ LL - type A: S<C<i32 = u32> = ()>; // Just one erroneous equality constraint
LL + type A: S<C = ()>; // Just one erroneous equality constraint
|

error[E0277]: the trait bound `(): Q` is not satisfied
--> $DIR/issue-102335-ty.rs:2:15
|
LL | type A: S<C<i32 = u32> = ()>; // Just one erroneous equality constraint
| ^^^^^^^^^^^^^^^^^ the trait `Q` is not implemented for `()`
|
help: this trait has no implementations, consider adding one
--> $DIR/issue-102335-ty.rs:15:1
|
LL | trait Q {}
| ^^^^^^^

error[E0229]: associated item constraints are not allowed here
--> $DIR/issue-102335-ty.rs:8:17
--> $DIR/issue-102335-ty.rs:9:17
|
LL | type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equality constraints
| ^^^^^^^^^ associated item constraint not allowed here
Expand All @@ -36,7 +48,7 @@ LL + type A: S<C<X = i32> = ()>; // More than one erroneous equality constra
|

error[E0229]: associated item constraints are not allowed here
--> $DIR/issue-102335-ty.rs:8:17
--> $DIR/issue-102335-ty.rs:9:17
|
LL | type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equality constraints
| ^^^^^^^^^ associated item constraint not allowed here
Expand All @@ -48,6 +60,19 @@ LL - type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equal
LL + type A: S<C<X = i32> = ()>; // More than one erroneous equality constraints
|

error: aborting due to 4 previous errors
error[E0277]: the trait bound `(): Q` is not satisfied
--> $DIR/issue-102335-ty.rs:9:15
|
LL | type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equality constraints
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Q` is not implemented for `()`
|
help: this trait has no implementations, consider adding one
--> $DIR/issue-102335-ty.rs:15:1
|
LL | trait Q {}
| ^^^^^^^

error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0229`.
Some errors have detailed explanations: E0229, E0277.
For more information about an error, try `rustc --explain E0229`.
2 changes: 2 additions & 0 deletions tests/ui/async-await/async-fn/dyn-in-return-type.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ trait T {
//~^ ERROR return type cannot be a trait object without pointer indirection
//~| HELP consider returning an `impl Trait` instead of a `dyn Trait`
//~| HELP alternatively, box the return type, and wrap all of the returned values in `Box::new`
//~| ERROR: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time [E0277]
//~| HELP: the trait `Sized` is not implemented for `(dyn Debug + 'static)`
Box::new("")
}
}
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/async-await/async-fn/dyn-in-return-type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ trait T {
//~^ ERROR return type cannot be a trait object without pointer indirection
//~| HELP consider returning an `impl Trait` instead of a `dyn Trait`
//~| HELP alternatively, box the return type, and wrap all of the returned values in `Box::new`
//~| ERROR: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time [E0277]
//~| HELP: the trait `Sized` is not implemented for `(dyn Debug + 'static)`
Box::new("")
}
}
Expand Down
15 changes: 12 additions & 3 deletions tests/ui/async-await/async-fn/dyn-in-return-type.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ LL | async fn f(&self) -> Box<dyn core::fmt::Debug> {
| ++++ +

error[E0746]: return type cannot be a trait object without pointer indirection
--> $DIR/dyn-in-return-type.rs:19:26
--> $DIR/dyn-in-return-type.rs:21:26
|
LL | async fn f(&self) -> dyn core::fmt::Debug {
| ^^^^^^^^^^^^^^^^^^^^
Expand All @@ -46,6 +46,15 @@ help: alternatively, box the return type, and wrap all of the returned values in
LL | async fn f(&self) -> Box<dyn core::fmt::Debug> {
| ++++ +

error: aborting due to 3 previous errors
error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time
--> $DIR/dyn-in-return-type.rs:11:26
|
LL | async fn f(&self) -> dyn core::fmt::Debug {
| ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Debug + 'static)`

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0746`.
Some errors have detailed explanations: E0277, E0746.
For more information about an error, try `rustc --explain E0277`.
2 changes: 1 addition & 1 deletion tests/ui/closures/issue-111932.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
trait Foo: std::fmt::Debug {}

fn print_foos(foos: impl Iterator<Item = dyn Foo>) {
fn print_foos(foos: impl Iterator<Item = dyn Foo>) { //~ ERROR [E0277]
foos.for_each(|foo| { //~ ERROR [E0277]
println!("{:?}", foo); //~ ERROR [E0277]
});
Expand Down
10 changes: 9 additions & 1 deletion tests/ui/closures/issue-111932.stderr
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
error[E0277]: the size for values of type `(dyn Foo + 'static)` cannot be known at compilation time
--> $DIR/issue-111932.rs:3:35
|
LL | fn print_foos(foos: impl Iterator<Item = dyn Foo>) {
| ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Foo + 'static)`

error[E0277]: the size for values of type `(dyn Foo + 'static)` cannot be known at compilation time
--> $DIR/issue-111932.rs:4:20
|
Expand All @@ -19,6 +27,6 @@ LL | println!("{:?}", foo);
= help: the trait `Sized` is not implemented for `dyn Foo`
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 2 previous errors
error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.
Loading
Loading