]> git.lizzy.rs Git - rust.git/commitdiff
Review comments and more tests
authorjackh726 <jack.huey@umassmed.edu>
Mon, 1 Nov 2021 01:36:04 +0000 (21:36 -0400)
committerjackh726 <jack.huey@umassmed.edu>
Mon, 1 Nov 2021 01:36:04 +0000 (21:36 -0400)
compiler/rustc_typeck/src/check/wfcheck.rs
src/test/ui/generic-associated-types/self-outlives-lint.rs
src/test/ui/generic-associated-types/self-outlives-lint.stderr

index 7680f8f9ca09771b2390fd80603477edd7aefdcd..36dbc04d0a6f524ebce7d6f664d568ce720ac98e 100644 (file)
@@ -266,6 +266,16 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
 /// Require that the user writes as where clauses on GATs the implicit
 /// outlives bounds involving trait parameters in trait functions and
 /// lifetimes passed as GAT substs. See `self-outlives-lint` test.
+///
+/// This trait will be our running example. We are currently WF checking the `Item` item...
+///
+/// ```rust
+/// trait LendingIterator {
+///   type Item<'me>; // <-- WF checking this trait item
+///
+///   fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
+/// }
+/// ```
 fn check_gat_where_clauses(
     tcx: TyCtxt<'_>,
     trait_item: &hir::TraitItem<'_>,
@@ -282,16 +292,25 @@ fn check_gat_where_clauses(
         return;
     }
     let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id);
-    let mut clauses = FxHashSet::default();
+    let mut clauses: Option<FxHashSet<ty::Predicate<'_>>> = None;
     // For every function in this trait...
+    // In our example, this would be the `next` method
     for item in
         associated_items.in_definition_order().filter(|item| matches!(item.kind, ty::AssocKind::Fn))
     {
+        // The clauses we that we would require from this function
+        let mut function_clauses = FxHashSet::default();
+
         let id = hir::HirId::make_owner(item.def_id.expect_local());
         let param_env = tcx.param_env(item.def_id.expect_local());
 
         let sig = tcx.fn_sig(item.def_id);
+        // Get the signature using placeholders. In our example, this would
+        // convert the late-bound 'a into a free region.
         let sig = tcx.liberate_late_bound_regions(item.def_id, sig);
+        // Collect the arguments that are given to this GAT in the return type
+        // of  the function signature. In our example, the GAT in the return
+        // type is `<Self as LendingIterator>::Item<'a>`, so 'a and Self are arguments.
         let mut visitor = GATSubstCollector {
             tcx,
             gat: trait_item.def_id.to_def_id(),
@@ -299,11 +318,30 @@ fn check_gat_where_clauses(
             types: FxHashSet::default(),
         };
         sig.output().visit_with(&mut visitor);
+
+        // If both regions and types are empty, then this GAT isn't in the
+        // return type, and we shouldn't try to do clause analysis
+        // (particularly, doing so would end up with an empty set of clauses,
+        // since the current method would require none, and we take the
+        // intersection of requirements of all methods)
+        if visitor.types.is_empty() && visitor.regions.is_empty() {
+            continue;
+        }
+
+        // The types we can assume to be well-formed. In our example, this
+        // would be &'a mut Self, from the first argument.
         let mut wf_tys = FxHashSet::default();
         wf_tys.extend(sig.inputs());
 
+        // For each region argument (e.g., 'a in our example), check for a
+        // relationship to the type arguments (e.g., Self). If there is an
+        // outlives relationship (`Self: 'a`), then we want to ensure that is
+        // reflected in a where clause on the GAT itself.
         for (region, region_idx) in &visitor.regions {
             for (ty, ty_idx) in &visitor.types {
+                // Unfortunately, we have to use a new `InferCtxt` for each
+                // pair, because region constraints get added and solved there,
+                // and we need to test each pair individually.
                 tcx.infer_ctxt().enter(|infcx| {
                     let mut outlives_environment = OutlivesEnvironment::new(param_env);
                     outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP);
@@ -328,6 +366,7 @@ fn check_gat_where_clauses(
                         Some(tcx.lifetimes.re_root_empty),
                         param_env,
                     );
+                    // In our example, requires that Self: 'a
                     outlives.type_must_outlive(origin, sup_type, sub_region);
 
                     let errors = infcx.resolve_regions(
@@ -338,14 +377,21 @@ fn check_gat_where_clauses(
 
                     debug!(?errors, "errors");
 
+                    // If we were able to prove that Self: 'a without an error,
+                    // it must be because of the implied or explicit bounds...
                     if errors.is_empty() {
                         debug!(?ty_idx, ?region_idx);
                         debug!("required clause: {} must outlive {}", ty, region);
+                        // Translate into the generic parameters of the GAT. In
+                        // our example, the type was Self, which will also be
+                        // Self in the GAT.
                         let ty_param = generics.param_at(*ty_idx, tcx);
                         let ty_param = tcx.mk_ty(ty::Param(ty::ParamTy {
                             index: ty_param.index,
                             name: ty_param.name,
                         }));
+                        // Same for the region. In our example, 'a corresponds
+                        // to the 'me parameter.
                         let region_param = generics.param_at(*region_idx, tcx);
                         let region_param =
                             tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion {
@@ -353,22 +399,35 @@ fn check_gat_where_clauses(
                                 index: region_param.index,
                                 name: region_param.name,
                             }));
+                        // The predicate we expect to see. (In our example,
+                        // `Self: 'me`.)
                         let clause = ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(
                             ty_param,
                             region_param,
                         ));
                         let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
-                        clauses.insert(clause);
+                        function_clauses.insert(clause);
                     }
                 });
             }
         }
+
+        match clauses.as_mut() {
+            Some(clauses) => {
+                clauses.drain_filter(|p| !function_clauses.contains(p));
+            }
+            None => {
+                clauses = Some(function_clauses);
+            }
+        }
     }
 
     // If there are any missing clauses, emit an error
+    let mut clauses = clauses.unwrap_or_default();
     debug!(?clauses);
     if !clauses.is_empty() {
-        let written_predicates: ty::GenericPredicates<'_> = tcx.predicates_of(trait_item.def_id);
+        let written_predicates: ty::GenericPredicates<'_> =
+            tcx.explicit_predicates_of(trait_item.def_id);
         let clauses: Vec<_> = clauses
             .drain_filter(|clause| {
                 written_predicates.predicates.iter().find(|p| &p.0 == clause).is_none()
@@ -402,6 +461,10 @@ fn check_gat_where_clauses(
     }
 }
 
+/// TypeVisitor that looks for uses of GATs like
+/// `<P0 as Trait<P1..Pn>>::GAT<Pn..Pm>` and adds the arguments `P0..Pm` into
+/// the two vectors, `regions` and `types` (depending on their kind). For each
+/// parameter `Pi` also track the index `i`.
 struct GATSubstCollector<'tcx> {
     tcx: TyCtxt<'tcx>,
     gat: DefId,
index eb2970522e65270fe02d59be667d5a35fa5f56d6..4de850384e7bbb28768aa98a0af5f4304cdfac41 100644 (file)
@@ -2,6 +2,8 @@
 
 // check-fail
 
+use std::fmt::Debug;
+
 // We have a `&'a self`, so we need a `Self: 'a`
 trait Iterable {
     type Item<'x>;
@@ -100,4 +102,70 @@ fn des<'a, T>(&self, data: &'a T) -> Self::Out<'a, T> {
 }
 */
 
+// Similar case to before, except with GAT.
+trait NoGat<'a> {
+    type Bar;
+    fn method(&'a self) -> Self::Bar;
+}
+
+// Lifetime is not on function; except `Self: 'a`
+trait TraitLifetime<'a> {
+    type Bar<'b>;
+    //~^ Missing required bounds
+    fn method(&'a self) -> Self::Bar<'a>;
+}
+
+// Like above, but we have a where clause that can prove what we want
+trait TraitLifetimeWhere<'a> where Self: 'a {
+    type Bar<'b>;
+    //~^ Missing required bounds
+    fn method(&'a self) -> Self::Bar<'a>;
+}
+
+// Explicit bound instead of implicit; we want to still error
+trait ExplicitBound {
+    type Bar<'b>;
+    //~^ Missing required bounds
+    fn method<'b>(&self, token: &'b ()) -> Self::Bar<'b> where Self: 'b;
+}
+
+// The use of the GAT here is not in the return, we don't want to error
+trait NotInReturn {
+    type Bar<'b>;
+    fn method<'b>(&'b self) where Self::Bar<'b>: Debug;
+}
+
+// We obviously error for `Iterator`, but we should also error for `Item`
+trait IterableTwo {
+    type Item<'a>;
+    type Iterator<'a>: Iterator<Item = Self::Item<'a>>;
+    //~^ Missing required bounds
+    fn iter<'a>(&'a self) -> Self::Iterator<'a>;
+}
+
+// We also should report region outlives clauses
+trait RegionOutlives {
+    type Bar<'a, 'b>;
+    //~^ Missing required bounds
+    fn foo<'x, 'y>(&self, input: &'x &'y ()) -> Self::Bar<'x, 'y>;
+}
+
+/*
+impl Foo for () {
+    type Bar<'a, 'b> = &'a &'b ();
+    fn foo<'x, 'y>(&self, input: &'x &'y ()) -> Self::Bar<'x, 'y> {
+        input
+    }
+}
+*/
+
+// If there are multiple methods that return the GAT, require a set of clauses
+// that can be satisfied by *all* methods
+trait MultipleMethods {
+    type Bar<'me>;
+
+    fn gimme<'a>(&'a self) -> Self::Bar<'a>;
+    fn gimme_default(&self) -> Self::Bar<'static>;
+}
+
 fn main() {}
index 3dacae65267cfff77355f5c187cc0eb735a73ad0..acccbf24319c05f306f0b4ff8999bdf5f98405c1 100644 (file)
@@ -1,5 +1,5 @@
 error: Missing required bounds on Item
-  --> $DIR/self-outlives-lint.rs:7:5
+  --> $DIR/self-outlives-lint.rs:9:5
    |
 LL |     type Item<'x>;
    |     ^^^^^^^^^^^^^-
@@ -7,7 +7,7 @@ LL |     type Item<'x>;
    |                  help: add the required where clauses: `where Self: 'x`
 
 error: Missing required bounds on Out
-  --> $DIR/self-outlives-lint.rs:23:5
+  --> $DIR/self-outlives-lint.rs:25:5
    |
 LL |     type Out<'x>;
    |     ^^^^^^^^^^^^-
@@ -15,7 +15,7 @@ LL |     type Out<'x>;
    |                 help: add the required where clauses: `where T: 'x`
 
 error: Missing required bounds on Out
-  --> $DIR/self-outlives-lint.rs:37:5
+  --> $DIR/self-outlives-lint.rs:39:5
    |
 LL |     type Out<'x>;
    |     ^^^^^^^^^^^^-
@@ -23,7 +23,7 @@ LL |     type Out<'x>;
    |                 help: add the required where clauses: `where T: 'x`
 
 error: Missing required bounds on Out
-  --> $DIR/self-outlives-lint.rs:44:5
+  --> $DIR/self-outlives-lint.rs:46:5
    |
 LL |     type Out<'x, 'y>;
    |     ^^^^^^^^^^^^^^^^-
@@ -31,7 +31,7 @@ LL |     type Out<'x, 'y>;
    |                     help: add the required where clauses: `where U: 'y, T: 'x`
 
 error: Missing required bounds on Out
-  --> $DIR/self-outlives-lint.rs:59:5
+  --> $DIR/self-outlives-lint.rs:61:5
    |
 LL |     type Out<'x, D>;
    |     ^^^^^^^^^^^^^^^-
@@ -39,7 +39,7 @@ LL |     type Out<'x, D>;
    |                    help: add the required where clauses: `where D: 'x`
 
 error: Missing required bounds on Out
-  --> $DIR/self-outlives-lint.rs:75:5
+  --> $DIR/self-outlives-lint.rs:77:5
    |
 LL |     type Out<'x, D>;
    |     ^^^^^^^^^^^^^^^-
@@ -47,12 +47,44 @@ LL |     type Out<'x, D>;
    |                    help: add the required where clauses: `where D: 'x`
 
 error: Missing required bounds on Out
-  --> $DIR/self-outlives-lint.rs:90:5
+  --> $DIR/self-outlives-lint.rs:92:5
    |
 LL |     type Out<'x, D>;
    |     ^^^^^^^^^^^^^^^-
    |                    |
    |                    help: add the required where clauses: `where D: 'x`
 
-error: aborting due to 7 previous errors
+error: Missing required bounds on Bar
+  --> $DIR/self-outlives-lint.rs:113:5
+   |
+LL |     type Bar<'b>;
+   |     ^^^^^^^^^^^^-
+   |                 |
+   |                 help: add the required where clauses: `where Self: 'b, Self: 'a`
+
+error: Missing required bounds on Bar
+  --> $DIR/self-outlives-lint.rs:120:5
+   |
+LL |     type Bar<'b>;
+   |     ^^^^^^^^^^^^-
+   |                 |
+   |                 help: add the required where clauses: `where Self: 'b, Self: 'a`
+
+error: Missing required bounds on Bar
+  --> $DIR/self-outlives-lint.rs:127:5
+   |
+LL |     type Bar<'b>;
+   |     ^^^^^^^^^^^^-
+   |                 |
+   |                 help: add the required where clauses: `where Self: 'b`
+
+error: Missing required bounds on Iterator
+  --> $DIR/self-outlives-lint.rs:141:5
+   |
+LL |     type Iterator<'a>: Iterator<Item = Self::Item<'a>>;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+   |                                                       |
+   |                                                       help: add the required where clauses: `where Self: 'a`
+
+error: aborting due to 11 previous errors