Skip to content

Commit

Permalink
Auto merge of #73905 - matthewjasper:projection-bounds-2, r=nikomatsakis
Browse files Browse the repository at this point in the history
Separate projection bounds and predicates

Follow up to #72788.

- Rename `projection_predicates` to `item_bounds`
- Separate bounds on associated types (the things after the `:` in `type X: ...`) and opaque types (the things after `impl`)  from predicates.
- Projection candidates now have the correct nested obligations
- Trait object candidates now check that the associated types on the trait object satisfy their bounds as nested obligations
- Type alias impl trait types are now checked (#73035)
- `feature(generic_associated_types)` no longer changes how we handle bounds (#73816)

Opening for a perf and crater runs.

r? `@nikomatsakis`
  • Loading branch information
bors committed Oct 6, 2020
2 parents 5849a7e + 69fc6d8 commit 08e2d46
Show file tree
Hide file tree
Showing 259 changed files with 3,604 additions and 2,336 deletions.
30 changes: 19 additions & 11 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,17 +432,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.with_catch_scope(body.id, |this| {
let mut block = this.lower_block_noalloc(body, true);

let try_span = this.mark_span_with_reason(
DesugaringKind::TryBlock,
body.span,
this.allow_try_trait.clone(),
);

// Final expression of the block (if present) or `()` with span at the end of block
let tail_expr = block
.expr
.take()
.unwrap_or_else(|| this.expr_unit(this.sess.source_map().end_point(try_span)));
let (try_span, tail_expr) = if let Some(expr) = block.expr.take() {
(
this.mark_span_with_reason(
DesugaringKind::TryBlock,
expr.span,
this.allow_try_trait.clone(),
),
expr,
)
} else {
let try_span = this.mark_span_with_reason(
DesugaringKind::TryBlock,
this.sess.source_map().end_point(body.span),
this.allow_try_trait.clone(),
);

(try_span, this.expr_unit(try_span))
};

let ok_wrapped_span =
this.mark_span_with_reason(DesugaringKind::TryBlock, tail_expr.span, None);
Expand Down Expand Up @@ -1553,7 +1561,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::LangItem::TryFromError,
unstable_span,
from_expr,
try_span,
unstable_span,
);
let thin_attrs = ThinVec::from(attrs);
let catch_scope = self.catch_scopes.last().copied();
Expand Down
38 changes: 15 additions & 23 deletions compiler/rustc_error_codes/src/error_codes/E0284.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,29 @@ as the `collect` method for `Iterator`s.
For example:

```compile_fail,E0284
fn foo() -> Result<bool, ()> {
let results = [Ok(true), Ok(false), Err(())].iter().cloned();
let v: Vec<bool> = results.collect()?;
// Do things with v...
Ok(true)
fn main() {
let n: u32 = 1;
let mut d: u64 = 2;
d = d + n.into();
}
```

Here we have an iterator `results` over `Result<bool, ()>`.
Hence, `results.collect()` can return any type implementing
`FromIterator<Result<bool, ()>>`. On the other hand, the
`?` operator can accept any type implementing `Try`.
Here we have an addition of `d` and `n.into()`. Hence, `n.into()` can return
any type `T` where `u64: Add<T>`. On the other hand, the `into` method can
return any type where `u32: Into<T>`.

The author of this code probably wants `collect()` to return a
`Result<Vec<bool>, ()>`, but the compiler can't be sure
that there isn't another type `T` implementing both `Try` and
`FromIterator<Result<bool, ()>>` in scope such that
`T::Ok == Vec<bool>`. Hence, this code is ambiguous and an error
is returned.
The author of this code probably wants `into()` to return a `u64`, but the
compiler can't be sure that there isn't another type `T` where both
`u32: Into<T>` and `u64: Add<T>`.

To resolve this error, use a concrete type for the intermediate expression:

```
fn foo() -> Result<bool, ()> {
let results = [Ok(true), Ok(false), Err(())].iter().cloned();
let v = {
let temp: Result<Vec<bool>, ()> = results.collect();
temp?
};
// Do things with v...
Ok(true)
fn main() {
let n: u32 = 1;
let mut d: u64 = 2;
let m: u64 = n.into();
d = d + m;
}
```

Expand Down
11 changes: 4 additions & 7 deletions compiler/rustc_infer/src/infer/higher_ranked/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! the end of the file for details.

use super::combine::CombineFields;
use super::{HigherRankedType, InferCtxt, PlaceholderMap};
use super::{HigherRankedType, InferCtxt};

use crate::infer::CombinedSnapshot;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
Expand Down Expand Up @@ -33,7 +33,7 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> {
self.infcx.commit_if_ok(|_| {
// First, we instantiate each bound region in the supertype with a
// fresh placeholder region.
let (b_prime, _) = self.infcx.replace_bound_vars_with_placeholders(&b);
let b_prime = self.infcx.replace_bound_vars_with_placeholders(&b);

// Next, we instantiate each bound region in the subtype
// with a fresh region variable. These region variables --
Expand Down Expand Up @@ -66,10 +66,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// the [rustc dev guide].
///
/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
pub fn replace_bound_vars_with_placeholders<T>(
&self,
binder: &ty::Binder<T>,
) -> (T, PlaceholderMap<'tcx>)
pub fn replace_bound_vars_with_placeholders<T>(&self, binder: &ty::Binder<T>) -> T
where
T: TypeFoldable<'tcx>,
{
Expand Down Expand Up @@ -122,7 +119,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
next_universe, binder, result, map,
);

(result, map)
result
}

/// See `infer::region_constraints::RegionConstraintCollector::leak_check`.
Expand Down
9 changes: 2 additions & 7 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,11 +351,6 @@ pub struct InferCtxt<'a, 'tcx> {
universe: Cell<ty::UniverseIndex>,
}

/// A map returned by `replace_bound_vars_with_placeholders()`
/// indicating the placeholder region that each late-bound region was
/// replaced with.
pub type PlaceholderMap<'tcx> = BTreeMap<ty::BoundRegion, ty::Region<'tcx>>;

/// See the `error_reporting` module for more details.
#[derive(Clone, Debug, PartialEq, Eq, TypeFoldable)]
pub enum ValuePairs<'tcx> {
Expand Down Expand Up @@ -992,7 +987,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}

Some(self.commit_if_ok(|_snapshot| {
let (ty::SubtypePredicate { a_is_expected, a, b }, _) =
let ty::SubtypePredicate { a_is_expected, a, b } =
self.replace_bound_vars_with_placeholders(&predicate);

let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?;
Expand All @@ -1007,7 +1002,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
predicate: ty::PolyRegionOutlivesPredicate<'tcx>,
) -> UnitResult<'tcx> {
self.commit_if_ok(|_snapshot| {
let (ty::OutlivesPredicate(r_a, r_b), _) =
let ty::OutlivesPredicate(r_a, r_b) =
self.replace_bound_vars_with_placeholders(&predicate);
let origin = SubregionOrigin::from_obligation_cause(cause, || {
RelateRegionParamBound(cause.span)
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_infer/src/infer/outlives/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
assoc_item_def_id: DefId,
) -> impl Iterator<Item = ty::Region<'tcx>> {
let tcx = self.tcx;
let predicates = tcx.projection_predicates(assoc_item_def_id);
predicates
let bounds = tcx.item_bounds(assoc_item_def_id);
bounds
.into_iter()
.filter_map(|p| p.to_opt_type_outlives())
.filter_map(|p| p.no_bound_vars())
Expand Down
31 changes: 18 additions & 13 deletions compiler/rustc_infer/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use crate::traits::{Obligation, ObligationCause, PredicateObligation};
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::ty::outlives::Component;
use rustc_middle::ty::{self, ToPredicate, TyCtxt, WithConstness};
use rustc_span::Span;

pub fn anonymize_predicate<'tcx>(
tcx: TyCtxt<'tcx>,
Expand Down Expand Up @@ -94,7 +93,11 @@ pub fn elaborate_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
) -> Elaborator<'tcx> {
let obligations = predicates.map(|predicate| predicate_obligation(predicate, None)).collect();
let obligations = predicates
.map(|predicate| {
predicate_obligation(predicate, ty::ParamEnv::empty(), ObligationCause::dummy())
})
.collect();
elaborate_obligations(tcx, obligations)
}

Expand All @@ -109,15 +112,10 @@ pub fn elaborate_obligations<'tcx>(

fn predicate_obligation<'tcx>(
predicate: ty::Predicate<'tcx>,
span: Option<Span>,
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
) -> PredicateObligation<'tcx> {
let cause = if let Some(span) = span {
ObligationCause::dummy_with_span(span)
} else {
ObligationCause::dummy()
};

Obligation { cause, param_env: ty::ParamEnv::empty(), recursion_depth: 0, predicate }
Obligation { cause, param_env, recursion_depth: 0, predicate }
}

impl Elaborator<'tcx> {
Expand All @@ -133,10 +131,11 @@ impl Elaborator<'tcx> {
// Get predicates declared on the trait.
let predicates = tcx.super_predicates_of(data.def_id());

let obligations = predicates.predicates.iter().map(|&(pred, span)| {
let obligations = predicates.predicates.iter().map(|&(pred, _)| {
predicate_obligation(
pred.subst_supertrait(tcx, &ty::Binder::bind(data.trait_ref)),
Some(span),
obligation.param_env,
obligation.cause.clone(),
)
});
debug!("super_predicates: data={:?}", data);
Expand Down Expand Up @@ -233,7 +232,13 @@ impl Elaborator<'tcx> {
})
.map(|predicate_kind| predicate_kind.to_predicate(tcx))
.filter(|&predicate| visited.insert(predicate))
.map(|predicate| predicate_obligation(predicate, None)),
.map(|predicate| {
predicate_obligation(
predicate,
obligation.param_env,
obligation.cause.clone(),
)
}),
);
}
ty::PredicateAtom::TypeWellFormedFromEnv(..) => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
ty::Adt(def, _) => check_must_use_def(cx, def.did, span, descr_pre, descr_post),
ty::Opaque(def, _) => {
let mut has_emitted = false;
for (predicate, _) in cx.tcx.predicates_of(def).predicates {
for &(predicate, _) in cx.tcx.explicit_item_bounds(def) {
// We only look at the `DefId`, so it is safe to skip the binder here.
if let ty::PredicateAtom::Trait(ref poly_trait_predicate, _) =
predicate.skip_binders()
Expand Down
15 changes: 14 additions & 1 deletion compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
.tables
.inferred_outlives
.get(self, item_id)
.map(|predicates| predicates.decode((self, tcx)))
.map(|predicates| tcx.arena.alloc_from_iter(predicates.decode((self, tcx))))
.unwrap_or_default()
}

Expand All @@ -949,6 +949,19 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
self.root.tables.super_predicates.get(self, item_id).unwrap().decode((self, tcx))
}

fn get_explicit_item_bounds(
&self,
item_id: DefIndex,
tcx: TyCtxt<'tcx>,
) -> &'tcx [(ty::Predicate<'tcx>, Span)] {
self.root
.tables
.explicit_item_bounds
.get(self, item_id)
.map(|bounds| tcx.arena.alloc_from_iter(bounds.decode((self, tcx))))
.unwrap_or_default()
}

fn get_generics(&self, item_id: DefIndex, sess: &Session) -> ty::Generics {
self.root.tables.generics.get(self, item_id).unwrap().decode((self, sess))
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
explicit_predicates_of => { cdata.get_explicit_predicates(def_id.index, tcx) }
inferred_outlives_of => { cdata.get_inferred_outlives(def_id.index, tcx) }
super_predicates_of => { cdata.get_super_predicates(def_id.index, tcx) }
explicit_item_bounds => { cdata.get_explicit_item_bounds(def_id.index, tcx) }
trait_def => { cdata.get_trait_def(def_id.index, tcx.sess) }
adt_def => { cdata.get_adt_def(def_id.index, tcx) }
adt_destructor => {
Expand Down
18 changes: 16 additions & 2 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,14 @@ impl EncodeContext<'a, 'tcx> {
record!(self.tables.super_predicates[def_id] <- self.tcx.super_predicates_of(def_id));
}

fn encode_explicit_item_bounds(&mut self, def_id: DefId) {
debug!("EncodeContext::encode_explicit_item_bounds({:?})", def_id);
let bounds = self.tcx.explicit_item_bounds(def_id);
if !bounds.is_empty() {
record!(self.tables.explicit_item_bounds[def_id] <- bounds);
}
}

fn encode_info_for_trait_item(&mut self, def_id: DefId) {
debug!("EncodeContext::encode_info_for_trait_item({:?})", def_id);
let tcx = self.tcx;
Expand Down Expand Up @@ -1017,7 +1025,10 @@ impl EncodeContext<'a, 'tcx> {
has_self: trait_item.fn_has_self_parameter,
}))
}
ty::AssocKind::Type => EntryKind::AssocType(container),
ty::AssocKind::Type => {
self.encode_explicit_item_bounds(def_id);
EntryKind::AssocType(container)
}
});
record!(self.tables.visibility[def_id] <- trait_item.vis);
record!(self.tables.span[def_id] <- ast_item.span);
Expand Down Expand Up @@ -1255,7 +1266,10 @@ impl EncodeContext<'a, 'tcx> {
hir::ItemKind::ForeignMod(_) => EntryKind::ForeignMod,
hir::ItemKind::GlobalAsm(..) => EntryKind::GlobalAsm,
hir::ItemKind::TyAlias(..) => EntryKind::Type,
hir::ItemKind::OpaqueTy(..) => EntryKind::OpaqueTy,
hir::ItemKind::OpaqueTy(..) => {
self.encode_explicit_item_bounds(def_id);
EntryKind::OpaqueTy
}
hir::ItemKind::Enum(..) => EntryKind::Enum(self.tcx.adt_def(def_id).repr),
hir::ItemKind::Struct(ref struct_def, _) => {
let adt_def = self.tcx.adt_def(def_id);
Expand Down
10 changes: 4 additions & 6 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,11 @@ define_tables! {
generics: Table<DefIndex, Lazy<ty::Generics>>,
explicit_predicates: Table<DefIndex, Lazy!(ty::GenericPredicates<'tcx>)>,
expn_that_defined: Table<DefIndex, Lazy<ExpnId>>,
// FIXME(eddyb) this would ideally be `Lazy<[...]>` but `ty::Predicate`
// doesn't handle shorthands in its own (de)serialization impls,
// as it's an `enum` for which we want to derive (de)serialization,
// so the `ty::codec` APIs handle the whole `&'tcx [...]` at once.
// Also, as an optimization, a missing entry indicates an empty `&[]`.
inferred_outlives: Table<DefIndex, Lazy!(&'tcx [(ty::Predicate<'tcx>, Span)])>,
// As an optimization, a missing entry indicates an empty `&[]`.
inferred_outlives: Table<DefIndex, Lazy!([(ty::Predicate<'tcx>, Span)])>,
super_predicates: Table<DefIndex, Lazy!(ty::GenericPredicates<'tcx>)>,
// As an optimization, a missing entry indicates an empty `&[]`.
explicit_item_bounds: Table<DefIndex, Lazy!([(ty::Predicate<'tcx>, Span)])>,
mir: Table<DefIndex, Lazy!(mir::Body<'tcx>)>,
promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::Body<'tcx>>)>,
mir_abstract_consts: Table<DefIndex, Lazy!(&'tcx [mir::abstract_const::Node<'tcx>])>,
Expand Down
Loading

0 comments on commit 08e2d46

Please sign in to comment.