From 97bf80dfda3627175b21bade3ba8e51b512c2186 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 24 Aug 2021 20:50:09 -0400 Subject: [PATCH] Treat types in unnormalized function signatures as well-formed --- .../type_check/free_region_relations.rs | 19 ++++++++++-- .../rustc_typeck/src/check/compare_method.rs | 12 +++++++- compiler/rustc_typeck/src/check/mod.rs | 16 +++++++--- compiler/rustc_typeck/src/check/regionck.rs | 11 ++++++- compiler/rustc_typeck/src/check/wfcheck.rs | 11 ++++++- .../generic-associated-types/issue-87748.rs | 30 +++++++++++++++++++ 6 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 src/test/ui/generic-associated-types/issue-87748.rs diff --git a/compiler/rustc_mir/src/borrow_check/type_check/free_region_relations.rs b/compiler/rustc_mir/src/borrow_check/type_check/free_region_relations.rs index 012d67255d13b..6426098d843fe 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/free_region_relations.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/free_region_relations.rs @@ -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(); diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index c384e0dcb2cae..d59291b8fd493 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -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(()) }) diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index a88b1c7af5a96..803c440bbc98b 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -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); >::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); } diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index ca6828cfdf68d..290fa5fc36719 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -144,11 +144,20 @@ impl<'a, 'tcx> FnCtxt<'a, '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 diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index b824370965928..0ac265396e3d0 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -908,6 +908,7 @@ fn check_where_clauses<'tcx, 'fcx>( } } +#[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 @@ const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut se `self: Rc`, `self: Arc`, or `self: Pin

` (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 index 0000000000000..93c3b3937cb81 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-87748.rs @@ -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 () {}