Skip to content
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

Add the generic_associated_types_extended feature #94869

Merged
merged 1 commit into from
Mar 31, 2022
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
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,8 @@ declare_features! (
(active, generic_arg_infer, "1.55.0", Some(85077), None),
/// Allows associated types to be generic, e.g., `type Foo<T>;` (RFC 1598).
(active, generic_associated_types, "1.23.0", Some(44265), None),
/// An extension to the `generic_associated_types` feature, allowing incomplete features.
(incomplete, generic_associated_types_extended, "1.61.0", Some(95451), None),
/// Allows non-trivial generic constants which have to have wfness manually propagated to callers
(incomplete, generic_const_exprs, "1.56.0", Some(76560), None),
/// Allows using `..X`, `..=X`, `...X`, and `X..` as a pattern.
Expand Down
47 changes: 47 additions & 0 deletions compiler/rustc_middle/src/ty/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1383,3 +1383,50 @@ impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector {
ControlFlow::CONTINUE
}
}

/// Finds the max universe present
pub struct MaxUniverse {
max_universe: ty::UniverseIndex,
}

impl MaxUniverse {
pub fn new() -> Self {
MaxUniverse { max_universe: ty::UniverseIndex::ROOT }
}

pub fn max_universe(self) -> ty::UniverseIndex {
self.max_universe
}
}

impl<'tcx> TypeVisitor<'tcx> for MaxUniverse {
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if let ty::Placeholder(placeholder) = t.kind() {
self.max_universe = ty::UniverseIndex::from_u32(
self.max_universe.as_u32().max(placeholder.universe.as_u32()),
);
}

t.super_visit_with(self)
}

fn visit_const(&mut self, c: ty::consts::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
if let ty::ConstKind::Placeholder(placeholder) = c.val() {
self.max_universe = ty::UniverseIndex::from_u32(
self.max_universe.as_u32().max(placeholder.universe.as_u32()),
);
}

c.super_visit_with(self)
}

fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
if let ty::RePlaceholder(placeholder) = *r {
self.max_universe = ty::UniverseIndex::from_u32(
self.max_universe.as_u32().max(placeholder.universe.as_u32()),
);
}

ControlFlow::CONTINUE
}
}
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,7 @@ symbols! {
generators,
generic_arg_infer,
generic_associated_types,
generic_associated_types_extended,
generic_const_exprs,
generic_param_attrs,
get_context,
Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_trait_selection/src/traits/auto_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use super::*;

use crate::infer::region_constraints::{Constraint, RegionConstraintData};
use crate::infer::InferCtxt;
use crate::traits::project::ProjectAndUnifyResult;
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::{Region, RegionVid, Term};

Expand Down Expand Up @@ -751,19 +752,19 @@ impl<'tcx> AutoTraitFinder<'tcx> {
debug!("Projecting and unifying projection predicate {:?}", predicate);

match project::poly_project_and_unify_type(select, &obligation.with(p)) {
Err(e) => {
ProjectAndUnifyResult::MismatchedProjectionTypes(e) => {
debug!(
"evaluate_nested_obligations: Unable to unify predicate \
'{:?}' '{:?}', bailing out",
ty, e
);
return false;
}
Ok(Err(project::InProgress)) => {
ProjectAndUnifyResult::Recursive => {
debug!("evaluate_nested_obligations: recursive projection predicate");
return false;
}
Ok(Ok(Some(v))) => {
ProjectAndUnifyResult::Holds(v) => {
// We only care about sub-obligations
// when we started out trying to unify
// some inference variables. See the comment above
Expand All @@ -782,7 +783,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
}
}
}
Ok(Ok(None)) => {
ProjectAndUnifyResult::FailedNormalization => {
// It's ok not to make progress when have no inference variables -
// in that case, we were only performing unifcation to check if an
// error occurred (which would indicate that it's impossible for our
Expand Down
12 changes: 7 additions & 5 deletions compiler/rustc_trait_selection/src/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_middle::ty::{self, Binder, Const, Ty, TypeFoldable};
use std::marker::PhantomData;

use super::const_evaluatable;
use super::project;
use super::project::{self, ProjectAndUnifyResult};
use super::select::SelectionContext;
use super::wf;
use super::CodeAmbiguity;
Expand Down Expand Up @@ -753,8 +753,8 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
}

match project::poly_project_and_unify_type(self.selcx, &project_obligation) {
Ok(Ok(Some(os))) => ProcessResult::Changed(mk_pending(os)),
Ok(Ok(None)) => {
ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(os)),
ProjectAndUnifyResult::FailedNormalization => {
stalled_on.clear();
stalled_on.extend(substs_infer_vars(
self.selcx,
Expand All @@ -763,10 +763,12 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
ProcessResult::Unchanged
}
// Let the caller handle the recursion
Ok(Err(project::InProgress)) => ProcessResult::Changed(mk_pending(vec![
ProjectAndUnifyResult::Recursive => ProcessResult::Changed(mk_pending(vec![
project_obligation.with(project_obligation.predicate.to_predicate(tcx)),
])),
Err(e) => ProcessResult::Error(CodeProjectionError(e)),
ProjectAndUnifyResult::MismatchedProjectionTypes(e) => {
ProcessResult::Error(CodeProjectionError(e))
}
}
}
}
Expand Down
76 changes: 56 additions & 20 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
use rustc_middle::ty::fold::{MaxUniverse, TypeFoldable, TypeFolder};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt};
use rustc_span::symbol::sym;
Expand Down Expand Up @@ -144,6 +144,18 @@ impl<'tcx> ProjectionCandidateSet<'tcx> {
}
}

/// Takes the place of a
/// Result<
/// Result<Option<Vec<PredicateObligation<'tcx>>>, InProgress>,
/// MismatchedProjectionTypes<'tcx>,
/// >
pub(super) enum ProjectAndUnifyResult<'tcx> {
Holds(Vec<PredicateObligation<'tcx>>),
FailedNormalization,
Recursive,
MismatchedProjectionTypes(MismatchedProjectionTypes<'tcx>),
}

/// Evaluates constraints of the form:
///
/// for<...> <T as Trait>::U == V
Expand All @@ -167,19 +179,47 @@ impl<'tcx> ProjectionCandidateSet<'tcx> {
pub(super) fn poly_project_and_unify_type<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &PolyProjectionObligation<'tcx>,
) -> Result<
Result<Option<Vec<PredicateObligation<'tcx>>>, InProgress>,
MismatchedProjectionTypes<'tcx>,
> {
) -> ProjectAndUnifyResult<'tcx> {
let infcx = selcx.infcx();
infcx.commit_if_ok(|_snapshot| {
let r = infcx.commit_if_ok(|_snapshot| {
let old_universe = infcx.universe();
let placeholder_predicate =
infcx.replace_bound_vars_with_placeholders(obligation.predicate);
let new_universe = infcx.universe();

let placeholder_obligation = obligation.with(placeholder_predicate);
let result = project_and_unify_type(selcx, &placeholder_obligation)?;
Ok(result)
})
match project_and_unify_type(selcx, &placeholder_obligation) {
ProjectAndUnifyResult::MismatchedProjectionTypes(e) => Err(e),
ProjectAndUnifyResult::Holds(obligations)
if old_universe != new_universe
&& selcx.tcx().features().generic_associated_types_extended =>
{
// If the `generic_associated_types_extended` feature is active, then we ignore any
// obligations references lifetimes from any universe greater than or equal to the
// universe just created. Otherwise, we can end up with something like `for<'a> I: 'a`,
// which isn't quite what we want. Ideally, we want either an implied
// `for<'a where I: 'a> I: 'a` or we want to "lazily" check these hold when we
// substitute concrete regions. There is design work to be done here; until then,
// however, this allows experimenting potential GAT features without running into
// well-formedness issues.
let new_obligations = obligations
.into_iter()
.filter(|obligation| {
let mut visitor = MaxUniverse::new();
compiler-errors marked this conversation as resolved.
Show resolved Hide resolved
obligation.predicate.visit_with(&mut visitor);
visitor.max_universe() < new_universe
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you leave a comment:

  1. saying that this is temporary/hack (or whatever you want to call it), and
  2. explaining what this "means" -- i.e. that this filters predicates referencing the newly replaced placeholder regions

Also, dumb question, what's the difference between visitor.max_universe() < new_universe and visitor.max_universe() <= old_universe? I guess it probably doesn't change behavior here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, the new_universe here refers to the universe we just created (from the replace_bound_vars_with_placeholders). Importantly, we want to skip any obligations containing this (so <= doesn't work, because = means it contains this new_universe). Any obligations with a max universe greater than new_universe must have been created from a nested HRTB (so maybe from with a fn). These are equally weird.

visitor.max_universe() < new_universe should be the same as visitor.max_universe() <= old_universe though, since new_universe should be old_universe+1, but that's more confusing IMO.

})
.collect();
Ok(ProjectAndUnifyResult::Holds(new_obligations))
}
other => Ok(other),
}
});

match r {
Ok(inner) => inner,
Err(err) => ProjectAndUnifyResult::MismatchedProjectionTypes(err),
}
}

/// Evaluates constraints of the form:
Expand All @@ -189,15 +229,11 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>(
/// If successful, this may result in additional obligations.
///
/// See [poly_project_and_unify_type] for an explanation of the return value.
#[tracing::instrument(level = "debug", skip(selcx))]
fn project_and_unify_type<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionObligation<'tcx>,
) -> Result<
Result<Option<Vec<PredicateObligation<'tcx>>>, InProgress>,
MismatchedProjectionTypes<'tcx>,
> {
debug!(?obligation, "project_and_unify_type");

) -> ProjectAndUnifyResult<'tcx> {
let mut obligations = vec![];

let infcx = selcx.infcx();
Expand All @@ -210,8 +246,8 @@ fn project_and_unify_type<'cx, 'tcx>(
&mut obligations,
) {
Ok(Some(n)) => n,
Ok(None) => return Ok(Ok(None)),
Err(InProgress) => return Ok(Err(InProgress)),
Ok(None) => return ProjectAndUnifyResult::FailedNormalization,
Err(InProgress) => return ProjectAndUnifyResult::Recursive,
};
debug!(?normalized, ?obligations, "project_and_unify_type result");
match infcx
Expand All @@ -220,11 +256,11 @@ fn project_and_unify_type<'cx, 'tcx>(
{
Ok(InferOk { obligations: inferred_obligations, value: () }) => {
obligations.extend(inferred_obligations);
Ok(Ok(Some(obligations)))
ProjectAndUnifyResult::Holds(obligations)
}
Err(err) => {
debug!("project_and_unify_type: equating types encountered error {:?}", err);
Err(MismatchedProjectionTypes { err })
debug!("equating types encountered error {:?}", err);
ProjectAndUnifyResult::MismatchedProjectionTypes(MismatchedProjectionTypes { err })
}
}
}
Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use super::{

use crate::infer::{InferCtxt, InferOk, TypeFreshener};
use crate::traits::error_reporting::InferCtxtExt;
use crate::traits::project::ProjectAndUnifyResult;
use crate::traits::project::ProjectionCacheKeyExt;
use crate::traits::ProjectionCacheKey;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
Expand Down Expand Up @@ -524,7 +525,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let data = bound_predicate.rebind(data);
let project_obligation = obligation.with(data);
match project::poly_project_and_unify_type(self, &project_obligation) {
Ok(Ok(Some(mut subobligations))) => {
ProjectAndUnifyResult::Holds(mut subobligations) => {
'compute_res: {
// If we've previously marked this projection as 'complete', then
// use the final cached result (either `EvaluatedToOk` or
Expand Down Expand Up @@ -572,9 +573,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
res
}
}
Ok(Ok(None)) => Ok(EvaluatedToAmbig),
Ok(Err(project::InProgress)) => Ok(EvaluatedToRecur),
Err(_) => Ok(EvaluatedToErr),
ProjectAndUnifyResult::FailedNormalization => Ok(EvaluatedToAmbig),
ProjectAndUnifyResult::Recursive => Ok(EvaluatedToRecur),
ProjectAndUnifyResult::MismatchedProjectionTypes(_) => Ok(EvaluatedToErr),
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#![feature(generic_associated_types)]

// This feature doesn't *currently* fire on any specific code; it's just a
// behavior change. Future changes might.
#[rustc_error] //~ the
fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0658]: the `#[rustc_error]` attribute is just used for rustc unit tests and will never be stable
--> $DIR/feature-gate-generic_associated_types_extended.rs:5:1
|
LL | #[rustc_error]
| ^^^^^^^^^^^^^^
|
= help: add `#![feature(rustc_attrs)]` to the crate attributes to enable

error: aborting due to previous error

For more information about this error, try `rustc --explain E0658`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0276]: impl has stricter requirements than trait
--> $DIR/lending_iterator.rs:14:45
|
LL | fn from_iter<T: for<'x> LendingIterator<Item<'x> = A>>(iter: T) -> Self;
| ------------------------------------------------------------------------ definition of `from_iter` from trait
...
LL | fn from_iter<I: for<'x> LendingIterator<Item<'x> = A>>(mut iter: I) -> Self {
| ^^^^^^^^^^^^ impl has extra requirement `I: 'x`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0276`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
error[E0276]: impl has stricter requirements than trait
--> $DIR/lending_iterator.rs:14:45
|
LL | fn from_iter<T: for<'x> LendingIterator<Item<'x> = A>>(iter: T) -> Self;
| ------------------------------------------------------------------------ definition of `from_iter` from trait
...
LL | fn from_iter<I: for<'x> LendingIterator<Item<'x> = A>>(mut iter: I) -> Self {
| ^^^^^^^^^^^^ impl has extra requirement `I: 'x`

error[E0311]: the parameter type `Self` may not live long enough
--> $DIR/lending_iterator.rs:35:9
|
LL | <B as FromLendingIterator<A>>::from_iter(self)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `Self: 'a`...
= note: ...so that the type `Self` will meet its required lifetime bounds...
note: ...that is required by this bound
--> $DIR/lending_iterator.rs:10:45
|
LL | fn from_iter<T: for<'x> LendingIterator<Item<'x> = A>>(iter: T) -> Self;
| ^^^^^^^^^^^^

error: aborting due to 2 previous errors

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