Skip to content

Commit d17c541

Browse files
committed
Resolve Self::AssocTy in impls
To do this we need to carry around the original resolution a bit, because `Self` gets resolved to the actual type immediately, but you're not allowed to write the equivalent type in a projection. (I tried just comparing the projection base type with the impl self type, but that seemed too dirty.) This is basically how rustc does it as well. Fixes #3249.
1 parent ce7496e commit d17c541

File tree

3 files changed

+93
-38
lines changed

3 files changed

+93
-38
lines changed

crates/ra_hir_ty/src/infer/path.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
4040
let ty = self.make_ty(type_ref);
4141
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
4242
let ctx = crate::lower::TyLoweringContext::new(self.db, &resolver);
43-
let ty = Ty::from_type_relative_path(&ctx, ty, remaining_segments_for_ty);
43+
let (ty, _) = Ty::from_type_relative_path(&ctx, ty, None, remaining_segments_for_ty);
4444
self.resolve_ty_assoc_item(
4545
ty,
4646
&path.segments().last().expect("path had at least one segment").name,
@@ -115,7 +115,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
115115
let remaining_segments_for_ty =
116116
remaining_segments.take(remaining_segments.len() - 1);
117117
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
118-
let ty = Ty::from_partly_resolved_hir_path(
118+
let (ty, _) = Ty::from_partly_resolved_hir_path(
119119
&ctx,
120120
def,
121121
resolved_segment,

crates/ra_hir_ty/src/lower.rs

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,14 @@ pub enum TypeParamLoweringMode {
9191

9292
impl Ty {
9393
pub fn from_hir(ctx: &TyLoweringContext<'_, impl HirDatabase>, type_ref: &TypeRef) -> Self {
94-
match type_ref {
94+
Ty::from_hir_ext(ctx, type_ref).0
95+
}
96+
pub fn from_hir_ext(
97+
ctx: &TyLoweringContext<'_, impl HirDatabase>,
98+
type_ref: &TypeRef,
99+
) -> (Self, Option<TypeNs>) {
100+
let mut res = None;
101+
let ty = match type_ref {
95102
TypeRef::Never => Ty::simple(TypeCtor::Never),
96103
TypeRef::Tuple(inner) => {
97104
let inner_tys: Arc<[Ty]> = inner.iter().map(|tr| Ty::from_hir(ctx, tr)).collect();
@@ -100,7 +107,11 @@ impl Ty {
100107
Substs(inner_tys),
101108
)
102109
}
103-
TypeRef::Path(path) => Ty::from_hir_path(ctx, path),
110+
TypeRef::Path(path) => {
111+
let (ty, res_) = Ty::from_hir_path(ctx, path);
112+
res = res_;
113+
ty
114+
}
104115
TypeRef::RawPtr(inner, mutability) => {
105116
let inner_ty = Ty::from_hir(ctx, inner);
106117
Ty::apply_one(TypeCtor::RawPtr(*mutability), inner_ty)
@@ -183,7 +194,8 @@ impl Ty {
183194
}
184195
}
185196
TypeRef::Error => Ty::Unknown,
186-
}
197+
};
198+
(ty, res)
187199
}
188200

189201
/// This is only for `generic_predicates_for_param`, where we can't just
@@ -217,17 +229,19 @@ impl Ty {
217229
pub(crate) fn from_type_relative_path(
218230
ctx: &TyLoweringContext<'_, impl HirDatabase>,
219231
ty: Ty,
232+
// We need the original resolution to lower `Self::AssocTy` correctly
233+
res: Option<TypeNs>,
220234
remaining_segments: PathSegments<'_>,
221-
) -> Ty {
235+
) -> (Ty, Option<TypeNs>) {
222236
if remaining_segments.len() == 1 {
223237
// resolve unselected assoc types
224238
let segment = remaining_segments.first().unwrap();
225-
Ty::select_associated_type(ctx, ty, segment)
239+
(Ty::select_associated_type(ctx, ty, res, segment), None)
226240
} else if remaining_segments.len() > 1 {
227241
// FIXME report error (ambiguous associated type)
228-
Ty::Unknown
242+
(Ty::Unknown, None)
229243
} else {
230-
ty
244+
(ty, res)
231245
}
232246
}
233247

@@ -236,14 +250,14 @@ impl Ty {
236250
resolution: TypeNs,
237251
resolved_segment: PathSegment<'_>,
238252
remaining_segments: PathSegments<'_>,
239-
) -> Ty {
253+
) -> (Ty, Option<TypeNs>) {
240254
let ty = match resolution {
241255
TypeNs::TraitId(trait_) => {
242256
// if this is a bare dyn Trait, we'll directly put the required ^0 for the self type in there
243257
let self_ty = if remaining_segments.len() == 0 { Some(Ty::Bound(0)) } else { None };
244258
let trait_ref =
245259
TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty);
246-
return if remaining_segments.len() == 1 {
260+
let ty = if remaining_segments.len() == 1 {
247261
let segment = remaining_segments.first().unwrap();
248262
let associated_ty = associated_type_by_name_including_super_traits(
249263
ctx.db,
@@ -269,6 +283,7 @@ impl Ty {
269283
} else {
270284
Ty::Dyn(Arc::new([GenericPredicate::Implemented(trait_ref)]))
271285
};
286+
return (ty, None);
272287
}
273288
TypeNs::GenericParam(param_id) => {
274289
let generics =
@@ -306,22 +321,25 @@ impl Ty {
306321
TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()),
307322
TypeNs::TypeAliasId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()),
308323
// FIXME: report error
309-
TypeNs::EnumVariantId(_) => return Ty::Unknown,
324+
TypeNs::EnumVariantId(_) => return (Ty::Unknown, None),
310325
};
311326

312-
Ty::from_type_relative_path(ctx, ty, remaining_segments)
327+
Ty::from_type_relative_path(ctx, ty, Some(resolution), remaining_segments)
313328
}
314329

315-
pub(crate) fn from_hir_path(ctx: &TyLoweringContext<'_, impl HirDatabase>, path: &Path) -> Ty {
330+
pub(crate) fn from_hir_path(
331+
ctx: &TyLoweringContext<'_, impl HirDatabase>,
332+
path: &Path,
333+
) -> (Ty, Option<TypeNs>) {
316334
// Resolve the path (in type namespace)
317335
if let Some(type_ref) = path.type_anchor() {
318-
let ty = Ty::from_hir(ctx, &type_ref);
319-
return Ty::from_type_relative_path(ctx, ty, path.segments());
336+
let (ty, res) = Ty::from_hir_ext(ctx, &type_ref);
337+
return Ty::from_type_relative_path(ctx, ty, res, path.segments());
320338
}
321339
let (resolution, remaining_index) =
322340
match ctx.resolver.resolve_path_in_type_ns(ctx.db, path.mod_path()) {
323341
Some(it) => it,
324-
None => return Ty::Unknown,
342+
None => return (Ty::Unknown, None),
325343
};
326344
let (resolved_segment, remaining_segments) = match remaining_index {
327345
None => (
@@ -336,31 +354,27 @@ impl Ty {
336354
fn select_associated_type(
337355
ctx: &TyLoweringContext<'_, impl HirDatabase>,
338356
self_ty: Ty,
357+
res: Option<TypeNs>,
339358
segment: PathSegment<'_>,
340359
) -> Ty {
341-
let def = match ctx.resolver.generic_def() {
342-
Some(def) => def,
343-
None => return Ty::Unknown, // this can't actually happen
344-
};
345-
let param_id = match self_ty {
346-
Ty::Placeholder(id) if ctx.type_param_mode == TypeParamLoweringMode::Placeholder => id,
347-
Ty::Bound(idx) if ctx.type_param_mode == TypeParamLoweringMode::Variable => {
348-
let generics = generics(ctx.db, def);
349-
let param_id = if let Some((id, _)) = generics.iter().nth(idx as usize) {
350-
id
351-
} else {
352-
return Ty::Unknown;
353-
};
354-
param_id
360+
let traits_from_env: Vec<_> = match res {
361+
Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) {
362+
None => return Ty::Unknown,
363+
Some(trait_ref) => vec![trait_ref.value.trait_],
364+
},
365+
Some(TypeNs::GenericParam(param_id)) => {
366+
let predicates = ctx.db.generic_predicates_for_param(param_id);
367+
predicates
368+
.iter()
369+
.filter_map(|pred| match &pred.value {
370+
GenericPredicate::Implemented(tr) => Some(tr.trait_),
371+
_ => None,
372+
})
373+
.collect()
355374
}
356-
_ => return Ty::Unknown, // Error: Ambiguous associated type
375+
_ => return Ty::Unknown,
357376
};
358-
let predicates = ctx.db.generic_predicates_for_param(param_id);
359-
let traits_from_env = predicates.iter().filter_map(|pred| match &pred.value {
360-
GenericPredicate::Implemented(tr) => Some(tr.trait_),
361-
_ => None,
362-
});
363-
let traits = traits_from_env.flat_map(|t| all_super_traits(ctx.db, t));
377+
let traits = traits_from_env.into_iter().flat_map(|t| all_super_traits(ctx.db, t));
364378
for t in traits {
365379
if let Some(associated_ty) = ctx.db.trait_data(t).associated_type_by_name(&segment.name)
366380
{

crates/ra_hir_ty/src/tests/traits.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1802,6 +1802,47 @@ fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> {
18021802
assert_eq!(t, "u32");
18031803
}
18041804

1805+
#[test]
1806+
fn unselected_projection_on_trait_self() {
1807+
assert_snapshot!(infer(
1808+
r#"
1809+
//- /main.rs
1810+
trait Trait {
1811+
type Item;
1812+
1813+
fn f(&self, x: Self::Item);
1814+
}
1815+
1816+
struct S;
1817+
1818+
impl Trait for S {
1819+
type Item = u32;
1820+
fn f(&self, x: Self::Item) { let y = x; }
1821+
}
1822+
1823+
struct S2;
1824+
1825+
impl Trait for S2 {
1826+
type Item = i32;
1827+
fn f(&self, x: <Self>::Item) { let y = x; }
1828+
}
1829+
"#,
1830+
), @r###"
1831+
[54; 58) 'self': &Self
1832+
[60; 61) 'x': {unknown}
1833+
[140; 144) 'self': &S
1834+
[146; 147) 'x': u32
1835+
[161; 175) '{ let y = x; }': ()
1836+
[167; 168) 'y': u32
1837+
[171; 172) 'x': u32
1838+
[242; 246) 'self': &S2
1839+
[248; 249) 'x': i32
1840+
[265; 279) '{ let y = x; }': ()
1841+
[271; 272) 'y': i32
1842+
[275; 276) 'x': i32
1843+
"###);
1844+
}
1845+
18051846
#[test]
18061847
fn trait_impl_self_ty() {
18071848
let t = type_at(

0 commit comments

Comments
 (0)