Skip to content

Commit e08ab08

Browse files
authored
Rollup merge of #94869 - jackh726:gats_extended, r=compiler-errors
Add the generic_associated_types_extended feature Right now, this only ignore obligations that reference new placeholders in `poly_project_and_unify_type`. In the future, this might do other things, like allowing object-safe GATs. **This feature is *incomplete* and quite likely unsound. This is mostly just for testing out potential future APIs using a "relaxed" set of rules until we figure out *proper* rules.** Also drive by cleanup of adding a `ProjectAndUnifyResult` enum instead of using a `Result<Result<Option>>`. r? `@nikomatsakis`
2 parents 943ea7f + 4e570a6 commit e08ab08

File tree

14 files changed

+261
-33
lines changed

14 files changed

+261
-33
lines changed

compiler/rustc_feature/src/active.rs

+2
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,8 @@ declare_features! (
400400
(active, generic_arg_infer, "1.55.0", Some(85077), None),
401401
/// Allows associated types to be generic, e.g., `type Foo<T>;` (RFC 1598).
402402
(active, generic_associated_types, "1.23.0", Some(44265), None),
403+
/// An extension to the `generic_associated_types` feature, allowing incomplete features.
404+
(incomplete, generic_associated_types_extended, "1.61.0", Some(95451), None),
403405
/// Allows non-trivial generic constants which have to have wfness manually propagated to callers
404406
(incomplete, generic_const_exprs, "1.56.0", Some(76560), None),
405407
/// Allows using `..X`, `..=X`, `...X`, and `X..` as a pattern.

compiler/rustc_middle/src/ty/fold.rs

+47
Original file line numberDiff line numberDiff line change
@@ -1379,3 +1379,50 @@ impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector {
13791379
ControlFlow::CONTINUE
13801380
}
13811381
}
1382+
1383+
/// Finds the max universe present
1384+
pub struct MaxUniverse {
1385+
max_universe: ty::UniverseIndex,
1386+
}
1387+
1388+
impl MaxUniverse {
1389+
pub fn new() -> Self {
1390+
MaxUniverse { max_universe: ty::UniverseIndex::ROOT }
1391+
}
1392+
1393+
pub fn max_universe(self) -> ty::UniverseIndex {
1394+
self.max_universe
1395+
}
1396+
}
1397+
1398+
impl<'tcx> TypeVisitor<'tcx> for MaxUniverse {
1399+
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
1400+
if let ty::Placeholder(placeholder) = t.kind() {
1401+
self.max_universe = ty::UniverseIndex::from_u32(
1402+
self.max_universe.as_u32().max(placeholder.universe.as_u32()),
1403+
);
1404+
}
1405+
1406+
t.super_visit_with(self)
1407+
}
1408+
1409+
fn visit_const(&mut self, c: ty::consts::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
1410+
if let ty::ConstKind::Placeholder(placeholder) = c.val() {
1411+
self.max_universe = ty::UniverseIndex::from_u32(
1412+
self.max_universe.as_u32().max(placeholder.universe.as_u32()),
1413+
);
1414+
}
1415+
1416+
c.super_visit_with(self)
1417+
}
1418+
1419+
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
1420+
if let ty::RePlaceholder(placeholder) = *r {
1421+
self.max_universe = ty::UniverseIndex::from_u32(
1422+
self.max_universe.as_u32().max(placeholder.universe.as_u32()),
1423+
);
1424+
}
1425+
1426+
ControlFlow::CONTINUE
1427+
}
1428+
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,7 @@ symbols! {
722722
generators,
723723
generic_arg_infer,
724724
generic_associated_types,
725+
generic_associated_types_extended,
725726
generic_const_exprs,
726727
generic_param_attrs,
727728
get_context,

compiler/rustc_trait_selection/src/traits/auto_trait.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use super::*;
55

66
use crate::infer::region_constraints::{Constraint, RegionConstraintData};
77
use crate::infer::InferCtxt;
8+
use crate::traits::project::ProjectAndUnifyResult;
89
use rustc_middle::ty::fold::TypeFolder;
910
use rustc_middle::ty::{Region, RegionVid, Term};
1011

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

753754
match project::poly_project_and_unify_type(select, &obligation.with(p)) {
754-
Err(e) => {
755+
ProjectAndUnifyResult::MismatchedProjectionTypes(e) => {
755756
debug!(
756757
"evaluate_nested_obligations: Unable to unify predicate \
757758
'{:?}' '{:?}', bailing out",
758759
ty, e
759760
);
760761
return false;
761762
}
762-
Ok(Err(project::InProgress)) => {
763+
ProjectAndUnifyResult::Recursive => {
763764
debug!("evaluate_nested_obligations: recursive projection predicate");
764765
return false;
765766
}
766-
Ok(Ok(Some(v))) => {
767+
ProjectAndUnifyResult::Holds(v) => {
767768
// We only care about sub-obligations
768769
// when we started out trying to unify
769770
// some inference variables. See the comment above
@@ -782,7 +783,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
782783
}
783784
}
784785
}
785-
Ok(Ok(None)) => {
786+
ProjectAndUnifyResult::FailedNormalization => {
786787
// It's ok not to make progress when have no inference variables -
787788
// in that case, we were only performing unification to check if an
788789
// error occurred (which would indicate that it's impossible for our

compiler/rustc_trait_selection/src/traits/fulfill.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_middle::ty::{self, Binder, Const, Ty, TypeFoldable};
1414
use std::marker::PhantomData;
1515

1616
use super::const_evaluatable;
17-
use super::project;
17+
use super::project::{self, ProjectAndUnifyResult};
1818
use super::select::SelectionContext;
1919
use super::wf;
2020
use super::CodeAmbiguity;
@@ -753,8 +753,8 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
753753
}
754754

755755
match project::poly_project_and_unify_type(self.selcx, &project_obligation) {
756-
Ok(Ok(Some(os))) => ProcessResult::Changed(mk_pending(os)),
757-
Ok(Ok(None)) => {
756+
ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(os)),
757+
ProjectAndUnifyResult::FailedNormalization => {
758758
stalled_on.clear();
759759
stalled_on.extend(substs_infer_vars(
760760
self.selcx,
@@ -763,10 +763,12 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
763763
ProcessResult::Unchanged
764764
}
765765
// Let the caller handle the recursion
766-
Ok(Err(project::InProgress)) => ProcessResult::Changed(mk_pending(vec![
766+
ProjectAndUnifyResult::Recursive => ProcessResult::Changed(mk_pending(vec![
767767
project_obligation.with(project_obligation.predicate.to_predicate(tcx)),
768768
])),
769-
Err(e) => ProcessResult::Error(CodeProjectionError(e)),
769+
ProjectAndUnifyResult::MismatchedProjectionTypes(e) => {
770+
ProcessResult::Error(CodeProjectionError(e))
771+
}
770772
}
771773
}
772774
}

compiler/rustc_trait_selection/src/traits/project.rs

+56-20
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use rustc_hir::def_id::DefId;
2828
use rustc_hir::lang_items::LangItem;
2929
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
3030
use rustc_middle::traits::select::OverflowError;
31-
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
31+
use rustc_middle::ty::fold::{MaxUniverse, TypeFoldable, TypeFolder};
3232
use rustc_middle::ty::subst::Subst;
3333
use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt};
3434
use rustc_span::symbol::sym;
@@ -144,6 +144,18 @@ impl<'tcx> ProjectionCandidateSet<'tcx> {
144144
}
145145
}
146146

147+
/// Takes the place of a
148+
/// Result<
149+
/// Result<Option<Vec<PredicateObligation<'tcx>>>, InProgress>,
150+
/// MismatchedProjectionTypes<'tcx>,
151+
/// >
152+
pub(super) enum ProjectAndUnifyResult<'tcx> {
153+
Holds(Vec<PredicateObligation<'tcx>>),
154+
FailedNormalization,
155+
Recursive,
156+
MismatchedProjectionTypes(MismatchedProjectionTypes<'tcx>),
157+
}
158+
147159
/// Evaluates constraints of the form:
148160
///
149161
/// for<...> <T as Trait>::U == V
@@ -167,19 +179,47 @@ impl<'tcx> ProjectionCandidateSet<'tcx> {
167179
pub(super) fn poly_project_and_unify_type<'cx, 'tcx>(
168180
selcx: &mut SelectionContext<'cx, 'tcx>,
169181
obligation: &PolyProjectionObligation<'tcx>,
170-
) -> Result<
171-
Result<Option<Vec<PredicateObligation<'tcx>>>, InProgress>,
172-
MismatchedProjectionTypes<'tcx>,
173-
> {
182+
) -> ProjectAndUnifyResult<'tcx> {
174183
let infcx = selcx.infcx();
175-
infcx.commit_if_ok(|_snapshot| {
184+
let r = infcx.commit_if_ok(|_snapshot| {
185+
let old_universe = infcx.universe();
176186
let placeholder_predicate =
177187
infcx.replace_bound_vars_with_placeholders(obligation.predicate);
188+
let new_universe = infcx.universe();
178189

179190
let placeholder_obligation = obligation.with(placeholder_predicate);
180-
let result = project_and_unify_type(selcx, &placeholder_obligation)?;
181-
Ok(result)
182-
})
191+
match project_and_unify_type(selcx, &placeholder_obligation) {
192+
ProjectAndUnifyResult::MismatchedProjectionTypes(e) => Err(e),
193+
ProjectAndUnifyResult::Holds(obligations)
194+
if old_universe != new_universe
195+
&& selcx.tcx().features().generic_associated_types_extended =>
196+
{
197+
// If the `generic_associated_types_extended` feature is active, then we ignore any
198+
// obligations references lifetimes from any universe greater than or equal to the
199+
// universe just created. Otherwise, we can end up with something like `for<'a> I: 'a`,
200+
// which isn't quite what we want. Ideally, we want either an implied
201+
// `for<'a where I: 'a> I: 'a` or we want to "lazily" check these hold when we
202+
// substitute concrete regions. There is design work to be done here; until then,
203+
// however, this allows experimenting potential GAT features without running into
204+
// well-formedness issues.
205+
let new_obligations = obligations
206+
.into_iter()
207+
.filter(|obligation| {
208+
let mut visitor = MaxUniverse::new();
209+
obligation.predicate.visit_with(&mut visitor);
210+
visitor.max_universe() < new_universe
211+
})
212+
.collect();
213+
Ok(ProjectAndUnifyResult::Holds(new_obligations))
214+
}
215+
other => Ok(other),
216+
}
217+
});
218+
219+
match r {
220+
Ok(inner) => inner,
221+
Err(err) => ProjectAndUnifyResult::MismatchedProjectionTypes(err),
222+
}
183223
}
184224

185225
/// Evaluates constraints of the form:
@@ -189,15 +229,11 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>(
189229
/// If successful, this may result in additional obligations.
190230
///
191231
/// See [poly_project_and_unify_type] for an explanation of the return value.
232+
#[tracing::instrument(level = "debug", skip(selcx))]
192233
fn project_and_unify_type<'cx, 'tcx>(
193234
selcx: &mut SelectionContext<'cx, 'tcx>,
194235
obligation: &ProjectionObligation<'tcx>,
195-
) -> Result<
196-
Result<Option<Vec<PredicateObligation<'tcx>>>, InProgress>,
197-
MismatchedProjectionTypes<'tcx>,
198-
> {
199-
debug!(?obligation, "project_and_unify_type");
200-
236+
) -> ProjectAndUnifyResult<'tcx> {
201237
let mut obligations = vec![];
202238

203239
let infcx = selcx.infcx();
@@ -210,8 +246,8 @@ fn project_and_unify_type<'cx, 'tcx>(
210246
&mut obligations,
211247
) {
212248
Ok(Some(n)) => n,
213-
Ok(None) => return Ok(Ok(None)),
214-
Err(InProgress) => return Ok(Err(InProgress)),
249+
Ok(None) => return ProjectAndUnifyResult::FailedNormalization,
250+
Err(InProgress) => return ProjectAndUnifyResult::Recursive,
215251
};
216252
debug!(?normalized, ?obligations, "project_and_unify_type result");
217253
let actual = obligation.predicate.term;
@@ -231,11 +267,11 @@ fn project_and_unify_type<'cx, 'tcx>(
231267
match infcx.at(&obligation.cause, obligation.param_env).eq(normalized, actual) {
232268
Ok(InferOk { obligations: inferred_obligations, value: () }) => {
233269
obligations.extend(inferred_obligations);
234-
Ok(Ok(Some(obligations)))
270+
ProjectAndUnifyResult::Holds(obligations)
235271
}
236272
Err(err) => {
237-
debug!("project_and_unify_type: equating types encountered error {:?}", err);
238-
Err(MismatchedProjectionTypes { err })
273+
debug!("equating types encountered error {:?}", err);
274+
ProjectAndUnifyResult::MismatchedProjectionTypes(MismatchedProjectionTypes { err })
239275
}
240276
}
241277
}

compiler/rustc_trait_selection/src/traits/select/mod.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use super::{
2121

2222
use crate::infer::{InferCtxt, InferOk, TypeFreshener};
2323
use crate::traits::error_reporting::InferCtxtExt;
24+
use crate::traits::project::ProjectAndUnifyResult;
2425
use crate::traits::project::ProjectionCacheKeyExt;
2526
use crate::traits::ProjectionCacheKey;
2627
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -525,7 +526,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
525526
let data = bound_predicate.rebind(data);
526527
let project_obligation = obligation.with(data);
527528
match project::poly_project_and_unify_type(self, &project_obligation) {
528-
Ok(Ok(Some(mut subobligations))) => {
529+
ProjectAndUnifyResult::Holds(mut subobligations) => {
529530
'compute_res: {
530531
// If we've previously marked this projection as 'complete', then
531532
// use the final cached result (either `EvaluatedToOk` or
@@ -573,9 +574,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
573574
res
574575
}
575576
}
576-
Ok(Ok(None)) => Ok(EvaluatedToAmbig),
577-
Ok(Err(project::InProgress)) => Ok(EvaluatedToRecur),
578-
Err(_) => Ok(EvaluatedToErr),
577+
ProjectAndUnifyResult::FailedNormalization => Ok(EvaluatedToAmbig),
578+
ProjectAndUnifyResult::Recursive => Ok(EvaluatedToRecur),
579+
ProjectAndUnifyResult::MismatchedProjectionTypes(_) => Ok(EvaluatedToErr),
579580
}
580581
}
581582

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#![feature(generic_associated_types)]
2+
3+
// This feature doesn't *currently* fire on any specific code; it's just a
4+
// behavior change. Future changes might.
5+
#[rustc_error] //~ the
6+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0658]: the `#[rustc_error]` attribute is just used for rustc unit tests and will never be stable
2+
--> $DIR/feature-gate-generic_associated_types_extended.rs:5:1
3+
|
4+
LL | #[rustc_error]
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0276]: impl has stricter requirements than trait
2+
--> $DIR/lending_iterator.rs:14:45
3+
|
4+
LL | fn from_iter<T: for<'x> LendingIterator<Item<'x> = A>>(iter: T) -> Self;
5+
| ------------------------------------------------------------------------ definition of `from_iter` from trait
6+
...
7+
LL | fn from_iter<I: for<'x> LendingIterator<Item<'x> = A>>(mut iter: I) -> Self {
8+
| ^^^^^^^^^^^^ impl has extra requirement `I: 'x`
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0276`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error[E0276]: impl has stricter requirements than trait
2+
--> $DIR/lending_iterator.rs:14:45
3+
|
4+
LL | fn from_iter<T: for<'x> LendingIterator<Item<'x> = A>>(iter: T) -> Self;
5+
| ------------------------------------------------------------------------ definition of `from_iter` from trait
6+
...
7+
LL | fn from_iter<I: for<'x> LendingIterator<Item<'x> = A>>(mut iter: I) -> Self {
8+
| ^^^^^^^^^^^^ impl has extra requirement `I: 'x`
9+
10+
error[E0311]: the parameter type `Self` may not live long enough
11+
--> $DIR/lending_iterator.rs:35:9
12+
|
13+
LL | <B as FromLendingIterator<A>>::from_iter(self)
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
|
16+
= help: consider adding an explicit lifetime bound `Self: 'a`...
17+
= note: ...so that the type `Self` will meet its required lifetime bounds...
18+
note: ...that is required by this bound
19+
--> $DIR/lending_iterator.rs:10:45
20+
|
21+
LL | fn from_iter<T: for<'x> LendingIterator<Item<'x> = A>>(iter: T) -> Self;
22+
| ^^^^^^^^^^^^
23+
24+
error: aborting due to 2 previous errors
25+
26+
For more information about this error, try `rustc --explain E0276`.

0 commit comments

Comments
 (0)