Skip to content

Commit 2c65a05

Browse files
bors[bot]flodiebold
andcommitted
Merge #1677
1677: Associated types r=flodiebold a=flodiebold This implements basic support for (fully qualified) associated type projections: - handle fully qualified paths like `<T as Trait>::AssocType` (basically desugaring to something like `Trait<Self=T>::AssocType`) - lower these to a new `Ty::Projection` enum variant - also introduce `Ty::UnselectedProjection` for cases like `T::AssocType` where the trait from which the type comes isn't specified, but these aren't handled further so far - in inference, normalize these projections using Chalk: basically, when encountering a type e.g. from a type annotation or signature, we replace these `Ty::Projection`s by type variables and add obligations to normalize the associated type Co-authored-by: Florian Diebold <[email protected]>
2 parents 0cf48e4 + 5af9691 commit 2c65a05

File tree

10 files changed

+405
-38
lines changed

10 files changed

+405
-38
lines changed

crates/ra_assists/src/auto_import.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ fn compare_path_segment(a: &SmolStr, b: &ast::PathSegment) -> bool {
7171
ast::PathSegmentKind::SelfKw => a == "self",
7272
ast::PathSegmentKind::SuperKw => a == "super",
7373
ast::PathSegmentKind::CrateKw => a == "crate",
74+
ast::PathSegmentKind::Type { .. } => false, // not allowed in imports
7475
}
7576
} else {
7677
false

crates/ra_hir/src/code_model.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,10 @@ impl TypeAlias {
838838
self.id.module(db)
839839
}
840840

841+
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
842+
self.module(db).krate(db)
843+
}
844+
841845
/// The containing impl block, if this is a method.
842846
pub fn impl_block(self, db: &impl DefDatabase) -> Option<ImplBlock> {
843847
let module_impls = db.impls_in_module(self.module(db));

crates/ra_hir/src/path.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ pub struct PathSegment {
2525
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2626
pub struct GenericArgs {
2727
pub args: Vec<GenericArg>,
28+
/// This specifies whether the args contain a Self type as the first
29+
/// element. This is the case for path segments like `<T as Trait>`, where
30+
/// `T` is actually a type parameter for the path `Trait` specifying the
31+
/// Self type. Otherwise, when we have a path `Trait<X, Y>`, the Self type
32+
/// is left out.
33+
pub has_self_type: bool,
2834
// someday also bindings
2935
}
3036

@@ -74,6 +80,28 @@ impl Path {
7480
let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
7581
segments.push(segment);
7682
}
83+
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
84+
assert!(path.qualifier().is_none()); // this can only occur at the first segment
85+
86+
// FIXME: handle <T> syntax (type segments without trait)
87+
88+
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
89+
let path = Path::from_ast(trait_ref?.path()?)?;
90+
kind = path.kind;
91+
let mut prefix_segments = path.segments;
92+
prefix_segments.reverse();
93+
segments.extend(prefix_segments);
94+
// Insert the type reference (T in the above example) as Self parameter for the trait
95+
let self_type = TypeRef::from_ast(type_ref?);
96+
let mut last_segment = segments.last_mut()?;
97+
if last_segment.args_and_bindings.is_none() {
98+
last_segment.args_and_bindings = Some(Arc::new(GenericArgs::empty()));
99+
};
100+
let args = last_segment.args_and_bindings.as_mut().unwrap();
101+
let mut args_inner = Arc::make_mut(args);
102+
args_inner.has_self_type = true;
103+
args_inner.args.insert(0, GenericArg::Type(self_type));
104+
}
77105
ast::PathSegmentKind::CrateKw => {
78106
kind = PathKind::Crate;
79107
break;
@@ -144,11 +172,15 @@ impl GenericArgs {
144172
}
145173
// lifetimes and assoc type args ignored for now
146174
if !args.is_empty() {
147-
Some(GenericArgs { args })
175+
Some(GenericArgs { args, has_self_type: false })
148176
} else {
149177
None
150178
}
151179
}
180+
181+
pub(crate) fn empty() -> GenericArgs {
182+
GenericArgs { args: Vec::new(), has_self_type: false }
183+
}
152184
}
153185

154186
impl From<Name> for Path {
@@ -236,6 +268,10 @@ fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
236268
}
237269
Path { kind: PathKind::Super, segments: Vec::new() }
238270
}
271+
ast::PathSegmentKind::Type { .. } => {
272+
// not allowed in imports
273+
return None;
274+
}
239275
};
240276
Some(res)
241277
}

crates/ra_hir/src/ty.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ pub enum TypeCtor {
9494

9595
/// A tuple type. For example, `(i32, bool)`.
9696
Tuple { cardinality: u16 },
97+
98+
/// Represents an associated item like `Iterator::Item`. This is used
99+
/// when we have tried to normalize a projection like `T::Item` but
100+
/// couldn't find a better representation. In that case, we generate
101+
/// an **application type** like `(Iterator::Item)<T>`.
102+
AssociatedType(TypeAlias),
97103
}
98104

99105
/// A nominal type with (maybe 0) type parameters. This might be a primitive
@@ -114,6 +120,12 @@ pub struct ProjectionTy {
114120
pub parameters: Substs,
115121
}
116122

123+
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
124+
pub struct UnselectedProjectionTy {
125+
pub type_name: Name,
126+
pub parameters: Substs,
127+
}
128+
117129
/// A type.
118130
///
119131
/// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents
@@ -127,6 +139,18 @@ pub enum Ty {
127139
/// several other things.
128140
Apply(ApplicationTy),
129141

142+
/// A "projection" type corresponds to an (unnormalized)
143+
/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
144+
/// trait and all its parameters are fully known.
145+
Projection(ProjectionTy),
146+
147+
/// This is a variant of a projection in which the trait is
148+
/// **not** known. It corresponds to a case where people write
149+
/// `T::Item` without specifying the trait. We would then try to
150+
/// figure out the trait by looking at all the traits that are in
151+
/// scope.
152+
UnselectedProjection(UnselectedProjectionTy),
153+
130154
/// A type parameter; for example, `T` in `fn f<T>(x: T) {}
131155
Param {
132156
/// The index of the parameter (starting with parameters from the
@@ -352,6 +376,16 @@ impl Ty {
352376
t.walk(f);
353377
}
354378
}
379+
Ty::Projection(p_ty) => {
380+
for t in p_ty.parameters.iter() {
381+
t.walk(f);
382+
}
383+
}
384+
Ty::UnselectedProjection(p_ty) => {
385+
for t in p_ty.parameters.iter() {
386+
t.walk(f);
387+
}
388+
}
355389
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
356390
}
357391
f(self);
@@ -362,6 +396,12 @@ impl Ty {
362396
Ty::Apply(a_ty) => {
363397
a_ty.parameters.walk_mut(f);
364398
}
399+
Ty::Projection(p_ty) => {
400+
p_ty.parameters.walk_mut(f);
401+
}
402+
Ty::UnselectedProjection(p_ty) => {
403+
p_ty.parameters.walk_mut(f);
404+
}
365405
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
366406
}
367407
f(self);
@@ -572,15 +612,61 @@ impl HirDisplay for ApplicationTy {
572612
write!(f, ">")?;
573613
}
574614
}
615+
TypeCtor::AssociatedType(type_alias) => {
616+
let trait_name = type_alias
617+
.parent_trait(f.db)
618+
.and_then(|t| t.name(f.db))
619+
.unwrap_or_else(Name::missing);
620+
let name = type_alias.name(f.db);
621+
write!(f, "{}::{}", trait_name, name)?;
622+
if self.parameters.len() > 0 {
623+
write!(f, "<")?;
624+
f.write_joined(&*self.parameters.0, ", ")?;
625+
write!(f, ">")?;
626+
}
627+
}
575628
}
576629
Ok(())
577630
}
578631
}
579632

633+
impl HirDisplay for ProjectionTy {
634+
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
635+
let trait_name = self
636+
.associated_ty
637+
.parent_trait(f.db)
638+
.and_then(|t| t.name(f.db))
639+
.unwrap_or_else(Name::missing);
640+
write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?;
641+
if self.parameters.len() > 1 {
642+
write!(f, "<")?;
643+
f.write_joined(&self.parameters[1..], ", ")?;
644+
write!(f, ">")?;
645+
}
646+
write!(f, ">::{}", self.associated_ty.name(f.db))?;
647+
Ok(())
648+
}
649+
}
650+
651+
impl HirDisplay for UnselectedProjectionTy {
652+
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
653+
write!(f, "{}", self.parameters[0].display(f.db))?;
654+
if self.parameters.len() > 1 {
655+
write!(f, "<")?;
656+
f.write_joined(&self.parameters[1..], ", ")?;
657+
write!(f, ">")?;
658+
}
659+
write!(f, "::{}", self.type_name)?;
660+
Ok(())
661+
}
662+
}
663+
580664
impl HirDisplay for Ty {
581665
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
582666
match self {
583667
Ty::Apply(a_ty) => a_ty.hir_fmt(f)?,
668+
Ty::Projection(p_ty) => p_ty.hir_fmt(f)?,
669+
Ty::UnselectedProjection(p_ty) => p_ty.hir_fmt(f)?,
584670
Ty::Param { name, .. } => write!(f, "{}", name)?,
585671
Ty::Bound(idx) => write!(f, "?{}", idx)?,
586672
Ty::Unknown => write!(f, "{{unknown}}")?,
@@ -606,3 +692,17 @@ impl HirDisplay for TraitRef {
606692
Ok(())
607693
}
608694
}
695+
696+
impl HirDisplay for Obligation {
697+
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
698+
match self {
699+
Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)),
700+
Obligation::Projection(proj) => write!(
701+
f,
702+
"Normalize({} => {})",
703+
proj.projection_ty.display(f.db),
704+
proj.ty.display(f.db)
705+
),
706+
}
707+
}
708+
}

crates/ra_hir/src/ty/infer.rs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
245245
&self.resolver,
246246
type_ref,
247247
);
248-
self.insert_type_vars(ty)
248+
let ty = self.insert_type_vars(ty);
249+
self.normalize_associated_types_in(ty)
249250
}
250251

251252
fn unify_substs(&mut self, substs1: &Substs, substs2: &Substs, depth: usize) -> bool {
@@ -411,6 +412,32 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
411412
ty
412413
}
413414

415+
/// Recurses through the given type, normalizing associated types mentioned
416+
/// in it by replacing them by type variables and registering obligations to
417+
/// resolve later. This should be done once for every type we get from some
418+
/// type annotation (e.g. from a let type annotation, field type or function
419+
/// call). `make_ty` handles this already, but e.g. for field types we need
420+
/// to do it as well.
421+
fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
422+
let ty = self.resolve_ty_as_possible(&mut vec![], ty);
423+
ty.fold(&mut |ty| match ty {
424+
Ty::Projection(proj_ty) => self.normalize_projection_ty(proj_ty),
425+
Ty::UnselectedProjection(proj_ty) => {
426+
// FIXME use Chalk's unselected projection support
427+
Ty::UnselectedProjection(proj_ty)
428+
}
429+
_ => ty,
430+
})
431+
}
432+
433+
fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
434+
let var = self.new_type_var();
435+
let predicate = ProjectionPredicate { projection_ty: proj_ty.clone(), ty: var.clone() };
436+
let obligation = Obligation::Projection(predicate);
437+
self.obligations.push(obligation);
438+
var
439+
}
440+
414441
/// Resolves the type completely; type variables without known type are
415442
/// replaced by Ty::Unknown.
416443
fn resolve_ty_completely(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
@@ -549,6 +576,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
549576
let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable);
550577
let ty = ty.subst(&substs);
551578
let ty = self.insert_type_vars(ty);
579+
let ty = self.normalize_associated_types_in(ty);
552580
Some(ty)
553581
}
554582
Resolution::LocalBinding(pat) => {
@@ -670,6 +698,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
670698
.and_then(|d| d.field(self.db, &Name::tuple_field_name(i)))
671699
.map_or(Ty::Unknown, |field| field.ty(self.db))
672700
.subst(&substs);
701+
let expected_ty = self.normalize_associated_types_in(expected_ty);
673702
self.infer_pat(subpat, &expected_ty, default_bm);
674703
}
675704

@@ -697,6 +726,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
697726
let matching_field = def.and_then(|it| it.field(self.db, &subpat.name));
698727
let expected_ty =
699728
matching_field.map_or(Ty::Unknown, |field| field.ty(self.db)).subst(&substs);
729+
let expected_ty = self.normalize_associated_types_in(expected_ty);
700730
self.infer_pat(subpat.pat, &expected_ty, default_bm);
701731
}
702732

@@ -927,9 +957,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
927957
self.unify(&expected_receiver_ty, &actual_receiver_ty);
928958

929959
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
930-
for (arg, param) in args.iter().zip(param_iter) {
931-
self.infer_expr(*arg, &Expectation::has_type(param));
960+
for (arg, param_ty) in args.iter().zip(param_iter) {
961+
let param_ty = self.normalize_associated_types_in(param_ty);
962+
self.infer_expr(*arg, &Expectation::has_type(param_ty));
932963
}
964+
let ret_ty = self.normalize_associated_types_in(ret_ty);
933965
ret_ty
934966
}
935967

@@ -1020,9 +1052,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
10201052
};
10211053
self.register_obligations_for_call(&callee_ty);
10221054
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
1023-
for (arg, param) in args.iter().zip(param_iter) {
1024-
self.infer_expr(*arg, &Expectation::has_type(param));
1055+
for (arg, param_ty) in args.iter().zip(param_iter) {
1056+
let param_ty = self.normalize_associated_types_in(param_ty);
1057+
self.infer_expr(*arg, &Expectation::has_type(param_ty));
10251058
}
1059+
let ret_ty = self.normalize_associated_types_in(ret_ty);
10261060
ret_ty
10271061
}
10281062
Expr::MethodCall { receiver, args, method_name, generic_args } => self
@@ -1120,7 +1154,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
11201154
_ => None,
11211155
})
11221156
.unwrap_or(Ty::Unknown);
1123-
self.insert_type_vars(ty)
1157+
let ty = self.insert_type_vars(ty);
1158+
self.normalize_associated_types_in(ty)
11241159
}
11251160
Expr::Await { expr } => {
11261161
let inner_ty = self.infer_expr(*expr, &Expectation::none());

0 commit comments

Comments
 (0)