From 4b164f681dd5f7fd216c57b427ffa5d4b2c72e55 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 8 Dec 2019 17:04:17 +0000 Subject: [PATCH] Correctly lower paths to generic associated types --- src/librustc/ty/mod.rs | 2 +- src/librustc/ty/sty.rs | 4 +- src/librustc_typeck/astconv.rs | 86 +++++++++++++++---- src/librustc_typeck/check/mod.rs | 12 ++- src/librustc_typeck/collect.rs | 11 ++- .../constrained_generic_params.rs | 2 +- 6 files changed, 95 insertions(+), 22 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 15bbfa7860fa..b105180b4875 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1305,7 +1305,7 @@ impl<'tcx> PolyProjectionPredicate<'tcx> { } #[inline] - pub fn to_poly_trait_ref(&self, tcx: TyCtxt<'_>) -> PolyTraitRef<'tcx> { + pub fn to_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> { // Note: unlike with `TraitRef::to_poly_trait_ref()`, // `self.0.trait_ref` is permitted to have escaping regions. // This is because here `self` has a `Binder` and so does our diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 6cb0d1e9946b..465262181f41 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1026,11 +1026,11 @@ impl<'tcx> ProjectionTy<'tcx> { /// Extracts the underlying trait reference from this projection. /// For example, if this is a projection of `::Item`, /// then this function would return a `T: Iterator` trait reference. - pub fn trait_ref(&self, tcx: TyCtxt<'_>) -> ty::TraitRef<'tcx> { + pub fn trait_ref(&self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { let def_id = tcx.associated_item(self.item_def_id).container.id(); ty::TraitRef { def_id, - substs: self.substs, + substs: self.substs.truncate_to(tcx, tcx.generics_of(def_id)), } } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 290f86d626e6..9b737428d5aa 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -88,6 +88,7 @@ pub trait AstConv<'tcx> { fn projected_ty_from_poly_trait_ref(&self, span: Span, item_def_id: DefId, + item_segment: &hir::PathSegment, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx>; @@ -205,6 +206,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let (substs, assoc_bindings, _) = self.create_substs_for_ast_path( span, def_id, + &[], item_segment.generic_args(), item_segment.infer_args, None, @@ -615,9 +617,21 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// `Output = u32` are returned in the `Vec` result. /// /// Note that the type listing given here is *exactly* what the user provided. + /// + /// For (generic) associated types + /// + /// ``` + /// as Iterable>::Iter::<'a> + /// ``` + /// + /// We have the parent substs are the substs for the parent trait: + /// `[Vec, u8]` and `generic_args` are the arguments for the associated + /// type itself: `['a]`. The returned `SubstsRef` concatenates these two + /// lists: `[Vec, u8, 'a]`. fn create_substs_for_ast_path<'a>(&self, span: Span, def_id: DefId, + parent_substs: &[subst::GenericArg<'tcx>], generic_args: &'a hir::GenericArgs, infer_args: bool, self_ty: Option>) @@ -633,17 +647,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let tcx = self.tcx(); let generic_params = tcx.generics_of(def_id); - // If a self-type was declared, one should be provided. - assert_eq!(generic_params.has_self, self_ty.is_some()); + if generic_params.has_self { + if generic_params.parent.is_some() { + // The parent is a trait so it should have at least one subst + // for the `Self` type. + assert!(!parent_substs.is_empty()) + } else { + // This item (presumably a trait) needs a self-type. + assert!(self_ty.is_some()); + } + } else { + assert!(self_ty.is_none() && parent_substs.is_empty()); + } - let has_self = generic_params.has_self; let (_, potential_assoc_types) = Self::check_generic_arg_count( tcx, span, &generic_params, &generic_args, GenericArgPosition::Type, - has_self, + self_ty.is_some(), infer_args, ); @@ -652,7 +675,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }); let default_needs_object_self = |param: &ty::GenericParamDef| { if let GenericParamDefKind::Type { has_default, .. } = param.kind { - if is_object && has_default && has_self { + if is_object && has_default { let self_param = tcx.types.self_param; if tcx.at(span).type_of(param.def_id).walk().any(|ty| ty == self_param) { // There is no suitable inference default for a type parameter @@ -668,7 +691,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let substs = Self::create_substs_for_generic_args( tcx, def_id, - &[][..], + parent_substs, self_ty.is_some(), self_ty, // Provide the generic args, and whether types should be inferred. @@ -780,6 +803,30 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { (substs, assoc_bindings, potential_assoc_types) } + crate fn create_substs_for_associated_item( + &self, + tcx: TyCtxt<'tcx>, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment, + parent_substs: SubstsRef<'tcx>, + ) -> SubstsRef<'tcx> { + if tcx.generics_of(item_def_id).params.is_empty() { + self.prohibit_generics(slice::from_ref(item_segment)); + + parent_substs + } else { + self.create_substs_for_ast_path( + span, + item_def_id, + parent_substs, + item_segment.generic_args(), + item_segment.infer_args, + None, + ).0 + } + } + /// Instantiates the path for the given trait reference, assuming that it's /// bound to a valid trait type. Returns the `DefId` of the defining trait. /// The type _cannot_ be a type other than a trait type. @@ -919,6 +966,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.create_substs_for_ast_path(span, trait_def_id, + &[], trait_segment.generic_args(), trait_segment.infer_args, Some(self_ty)) @@ -1665,8 +1713,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!("associated_path_to_ty: {:?}::{}", qself_ty, assoc_ident); - self.prohibit_generics(slice::from_ref(assoc_segment)); - // Check if we have an enum variant. let mut variant_resolution = None; if let ty::Adt(adt_def, _) = qself_ty.kind { @@ -1677,6 +1723,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if let Some(variant_def) = variant_def { if permit_variants { tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span); + self.prohibit_generics(slice::from_ref(assoc_segment)); return Ok((qself_ty, DefKind::Variant, variant_def.def_id)); } else { variant_resolution = Some(variant_def.def_id); @@ -1767,7 +1814,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { i.ident.modern() == assoc_ident }).expect("missing associated type"); - let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, bound); + let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, assoc_segment, bound); let ty = self.normalize_ty(span, ty); let kind = DefKind::AssocTy; @@ -1818,8 +1865,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!("qpath_to_ty: trait_def_id={:?}", trait_def_id); - self.prohibit_generics(slice::from_ref(item_segment)); - let self_ty = if let Some(ty) = opt_self_ty { ty } else { @@ -1861,9 +1906,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty, trait_segment); + let item_substs = self.create_substs_for_associated_item( + tcx, + span, + item_def_id, + item_segment, + trait_ref.substs, + ); + debug!("qpath_to_ty: trait_ref={:?}", trait_ref); - self.normalize_ty(span, tcx.mk_projection(item_def_id, trait_ref.substs)) + self.normalize_ty(span, tcx.mk_projection(item_def_id, item_substs)) } pub fn prohibit_generics<'a, T: IntoIterator>( @@ -2518,10 +2571,10 @@ impl<'tcx> Bounds<'tcx> { // If it could be sized, and is, add the `Sized` predicate. let sized_predicate = self.implicitly_sized.and_then(|span| { tcx.lang_items().sized_trait().map(|sized| { - let trait_ref = ty::TraitRef { + let trait_ref = ty::Binder::bind(ty::TraitRef { def_id: sized, substs: tcx.mk_substs_trait(param_ty, &[]) - }; + }); (trait_ref.to_predicate(), span) }) }); @@ -2529,10 +2582,11 @@ impl<'tcx> Bounds<'tcx> { sized_predicate.into_iter().chain( self.region_bounds.iter().map(|&(region_bound, span)| { // Account for the binder being introduced below; no need to shift `param_ty` - // because, at present at least, it can only refer to early-bound regions. + // because, at present at least, it either only refers to early-bound regions, + // or it's a generic associated type that deliberately has escaping bound vars. let region_bound = ty::fold::shift_region(tcx, region_bound, 1); let outlives = ty::OutlivesPredicate(param_ty, region_bound); - (ty::Binder::dummy(outlives).to_predicate(), span) + (ty::Binder::bind(outlives).to_predicate(), span) }).chain( self.trait_bounds.iter().map(|&(bound_trait_ref, span)| { (bound_trait_ref.to_predicate(), span) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 113d3f273233..c96ace5881e6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2613,6 +2613,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { fn projected_ty_from_poly_trait_ref(&self, span: Span, item_def_id: DefId, + item_segment: &hir::PathSegment, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> { @@ -2622,7 +2623,16 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { &poly_trait_ref ); - self.tcx().mk_projection(item_def_id, trait_ref.substs) + let item_substs = >::create_substs_for_associated_item( + self, + self.tcx, + span, + item_def_id, + item_segment, + trait_ref.substs, + ); + + self.tcx().mk_projection(item_def_id, item_substs) } fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index b9829793cbe5..27285632fdc6 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -224,10 +224,19 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { &self, span: Span, item_def_id: DefId, + item_segment: &hir::PathSegment, poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx> { if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { - self.tcx().mk_projection(item_def_id, trait_ref.substs) + let item_substs = >::create_substs_for_associated_item( + self, + self.tcx, + span, + item_def_id, + item_segment, + trait_ref.substs, + ); + self.tcx().mk_projection(item_def_id, item_substs) } else { // There are no late-bound regions; we can just ignore the binder. span_err!( diff --git a/src/librustc_typeck/constrained_generic_params.rs b/src/librustc_typeck/constrained_generic_params.rs index 0523de56512a..a3a703cf50e9 100644 --- a/src/librustc_typeck/constrained_generic_params.rs +++ b/src/librustc_typeck/constrained_generic_params.rs @@ -135,7 +135,7 @@ pub fn identify_constrained_generic_params<'tcx>( /// by 0. I should probably pick a less tangled example, but I can't /// think of any. pub fn setup_constraining_predicates<'tcx>( - tcx: TyCtxt<'_>, + tcx: TyCtxt<'tcx>, predicates: &mut [(ty::Predicate<'tcx>, Span)], impl_trait_ref: Option>, input_parameters: &mut FxHashSet,