]> git.lizzy.rs Git - rust.git/commitdiff
Treat types in unnormalized function signatures as well-formed
authorjackh726 <jack.huey@umassmed.edu>
Wed, 25 Aug 2021 00:50:09 +0000 (20:50 -0400)
committerjackh726 <jack.huey@umassmed.edu>
Thu, 26 Aug 2021 14:59:20 +0000 (10:59 -0400)
compiler/rustc_mir/src/borrow_check/type_check/free_region_relations.rs
compiler/rustc_typeck/src/check/compare_method.rs
compiler/rustc_typeck/src/check/mod.rs
compiler/rustc_typeck/src/check/regionck.rs
compiler/rustc_typeck/src/check/wfcheck.rs
src/test/ui/generic-associated-types/issue-87748.rs [new file with mode: 0644]

index 012d67255d13bd6b96171cbb0a48104d6c41eb68..6426098d843fe80fd2dd709de1a3dcd3b7fc7fab 100644 (file)
@@ -256,6 +256,9 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
         let constraint_sets: Vec<_> = unnormalized_input_output_tys
             .flat_map(|ty| {
                 debug!("build: input_or_output={:?}", ty);
+                // We add implied bounds from both the unnormalized and normalized ty
+                // See issue #87748
+                let constraints_implied_1 = self.add_implied_bounds(ty);
                 let TypeOpOutput { output: ty, constraints: constraints1, .. } = self
                     .param_env
                     .and(type_op::normalize::Normalize::new(ty))
@@ -271,9 +274,21 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
                             canonicalized_query: None,
                         }
                     });
-                let constraints2 = self.add_implied_bounds(ty);
+                // Note: we need this in examples like
+                // ```
+                // trait Foo {
+                //   type Bar;
+                //   fn foo(&self) -> &Self::Bar;
+                // }
+                // impl Foo for () {
+                //   type Bar = ();
+                //   fn foo(&self) ->&() {}
+                // }
+                // ```
+                // Both &Self::Bar and &() are WF
+                let constraints_implied_2 = self.add_implied_bounds(ty);
                 normalized_inputs_and_output.push(ty);
-                constraints1.into_iter().chain(constraints2)
+                constraints1.into_iter().chain(constraints_implied_1).chain(constraints_implied_2)
             })
             .collect();
 
index c384e0dcb2cae418bf238647e14d776166823917..d59291b8fd493e1a1c2106e7c629585ac3ff167a 100644 (file)
@@ -250,6 +250,8 @@ fn compare_predicate_entailment<'tcx>(
         // Compute placeholder form of impl and trait method tys.
         let tcx = infcx.tcx;
 
+        let mut wf_tys = vec![];
+
         let (impl_sig, _) = infcx.replace_bound_vars_with_fresh_vars(
             impl_m_span,
             infer::HigherRankedType,
@@ -260,10 +262,18 @@ fn compare_predicate_entailment<'tcx>(
         let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
         debug!("compare_impl_method: impl_fty={:?}", impl_fty);
 
+        // First liberate late bound regions and subst placeholders
         let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, tcx.fn_sig(trait_m.def_id));
         let trait_sig = trait_sig.subst(tcx, trait_to_placeholder_substs);
+        // Next, add all inputs and output as well-formed tys. Importantly,
+        // we have to do this before normalization, since the normalized ty may
+        // not contain the input parameters. See issue #87748.
+        wf_tys.extend(trait_sig.inputs_and_output.iter());
         let trait_sig =
             inh.normalize_associated_types_in(impl_m_span, impl_m_hir_id, param_env, trait_sig);
+        // Also add the resulting inputs and output as well-formed.
+        // This probably isn't strictly necessary.
+        wf_tys.extend(trait_sig.inputs_and_output.iter());
         let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));
 
         debug!("compare_impl_method: trait_fty={:?}", trait_fty);
@@ -388,7 +398,7 @@ fn compare_predicate_entailment<'tcx>(
         // Finally, resolve all regions. This catches wily misuses of
         // lifetime parameters.
         let fcx = FnCtxt::new(&inh, param_env, impl_m_hir_id);
-        fcx.regionck_item(impl_m_hir_id, impl_m_span, trait_sig.inputs_and_output);
+        fcx.regionck_item(impl_m_hir_id, impl_m_span, &wf_tys);
 
         Ok(())
     })
index a88b1c7af5a96640f49b498fd536d0524ea57834..803c440bbc98b3ae6f06aed391238390d38ea9df 100644 (file)
@@ -364,7 +364,7 @@ fn typeck_with_fallback<'tcx>(
 
     let typeck_results = Inherited::build(tcx, def_id).enter(|inh| {
         let param_env = tcx.param_env(def_id);
-        let fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
+        let (fcx, wf_tys) = if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
             let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() {
                 let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
                 <dyn AstConv<'_>>::ty_of_fn(
@@ -383,17 +383,25 @@ fn typeck_with_fallback<'tcx>(
 
             check_abi(tcx, id, span, fn_sig.abi());
 
+            // When normalizing the function signature, we assume all types are
+            // well-formed. So, we don't need to worry about the obligations
+            // from normalization. We could just discard these, but to align with
+            // compare_method and elsewhere, we just add implied bounds for
+            // these types.
+            let mut wf_tys = vec![];
             // Compute the fty from point of view of inside the fn.
             let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
+            wf_tys.extend(fn_sig.inputs_and_output.iter());
             let fn_sig = inh.normalize_associated_types_in(
                 body.value.span,
                 body_id.hir_id,
                 param_env,
                 fn_sig,
             );
+            wf_tys.extend(fn_sig.inputs_and_output.iter());
 
             let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0;
-            fcx
+            (fcx, wf_tys)
         } else {
             let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
             let expected_type = body_ty
@@ -443,7 +451,7 @@ fn typeck_with_fallback<'tcx>(
 
             fcx.write_ty(id, expected_type);
 
-            fcx
+            (fcx, vec![])
         };
 
         let fallback_has_occurred = fcx.type_inference_fallback();
@@ -467,7 +475,7 @@ fn typeck_with_fallback<'tcx>(
         fcx.select_all_obligations_or_error();
 
         if fn_sig.is_some() {
-            fcx.regionck_fn(id, body);
+            fcx.regionck_fn(id, body, span, &wf_tys);
         } else {
             fcx.regionck_expr(body);
         }
index ca6828cfdf68de599fee44ff104038bbb081a59c..290fa5fc36719278954ab0e9963399234d2d9ff3 100644 (file)
@@ -144,11 +144,20 @@ pub fn regionck_item(&self, item_id: hir::HirId, span: Span, wf_tys: &[Ty<'tcx>]
     /// rest of type check and because sometimes we need type
     /// inference to have completed before we can determine which
     /// constraints to add.
-    pub fn regionck_fn(&self, fn_id: hir::HirId, body: &'tcx hir::Body<'tcx>) {
+    pub(crate) fn regionck_fn(
+        &self,
+        fn_id: hir::HirId,
+        body: &'tcx hir::Body<'tcx>,
+        span: Span,
+        wf_tys: &[Ty<'tcx>],
+    ) {
         debug!("regionck_fn(id={})", fn_id);
         let subject = self.tcx.hir().body_owner_def_id(body.id());
         let hir_id = body.value.hir_id;
         let mut rcx = RegionCtxt::new(self, hir_id, Subject(subject), self.param_env);
+        // We need to add the implied bounds from the function signature
+        rcx.outlives_environment.add_implied_bounds(self, wf_tys, fn_id, span);
+        rcx.outlives_environment.save_implied_bounds(fn_id);
 
         if !self.errors_reported_since_creation() {
             // regionck assumes typeck succeeded
index b82437096592807b823b2f966ca3cf1bf80cb7da..0ac265396e3d0778d78cc6f880974ef4b88ddac1 100644 (file)
@@ -908,6 +908,7 @@ fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy
     }
 }
 
+#[tracing::instrument(level = "debug", skip(fcx, span, hir_decl))]
 fn check_fn_or_method<'fcx, 'tcx>(
     fcx: &FnCtxt<'fcx, 'tcx>,
     span: Span,
@@ -918,6 +919,11 @@ fn check_fn_or_method<'fcx, 'tcx>(
 ) {
     let sig = fcx.tcx.liberate_late_bound_regions(def_id, sig);
 
+    // Unnormalized types in signature are WF too
+    implied_bounds.extend(sig.inputs());
+    // FIXME(#27579) return types should not be implied bounds
+    implied_bounds.push(sig.output());
+
     // Normalize the input and output types one at a time, using a different
     // `WellFormedLoc` for each. We cannot call `normalize_associated_types`
     // on the entire `FnSig`, since this would use the same `WellFormedLoc`
@@ -967,9 +973,11 @@ fn check_fn_or_method<'fcx, 'tcx>(
         ObligationCauseCode::ReturnType,
     );
 
-    // FIXME(#25759) return types should not be implied bounds
+    // FIXME(#27579) return types should not be implied bounds
     implied_bounds.push(sig.output());
 
+    debug!(?implied_bounds);
+
     check_where_clauses(fcx, span, def_id, Some((sig.output(), hir_decl.output.span())));
 }
 
@@ -1116,6 +1124,7 @@ fn check_opaque_types<'fcx, 'tcx>(
      `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \
      of the previous types except `Self`)";
 
+#[tracing::instrument(level = "debug", skip(fcx))]
 fn check_method_receiver<'fcx, 'tcx>(
     fcx: &FnCtxt<'fcx, 'tcx>,
     fn_sig: &hir::FnSig<'_>,
diff --git a/src/test/ui/generic-associated-types/issue-87748.rs b/src/test/ui/generic-associated-types/issue-87748.rs
new file mode 100644 (file)
index 0000000..93c3b39
--- /dev/null
@@ -0,0 +1,30 @@
+// Checks that we properly add implied bounds from unnormalized projections in
+// inputs when typechecking functions.
+
+// check-pass
+
+#![feature(generic_associated_types)]
+
+trait MyTrait {
+    type Assoc<'a, 'b> where 'b: 'a;
+    fn do_sth(arg: Self::Assoc<'_, '_>);
+}
+
+struct A;
+struct B;
+struct C;
+
+impl MyTrait for A {
+    type Assoc<'a, 'b> where 'b: 'a = u32;
+    fn do_sth(_: u32) {}
+}
+impl MyTrait for B {
+    type Assoc<'a, 'b> where 'b: 'a = u32;
+    fn do_sth(_: Self::Assoc<'_, '_>) {}
+}
+impl MyTrait for C {
+    type Assoc<'a, 'b> where 'b: 'a = u32;
+    fn do_sth(_: Self::Assoc<'static, 'static>) {}
+}
+
+fn main () {}