Skip to content

Commit 01836a0

Browse files
Merge #3050
3050: Refactor type parameters, implement argument position impl trait r=matklad a=flodiebold I wanted to implement APIT by lowering to type parameters because we need to do that anyway for correctness and don't need Chalk support for it; this grew into some more wide-ranging refactoring of how type parameters are handled 😅 - use Ty::Bound instead of Ty::Param to represent polymorphism, and explicitly count binders. This gets us closer to Chalk's way of doing things, and means that we now only use Param as a placeholder for an unknown type, e.g. within a generic function. I.e. we're never using Param in a situation where we want to substitute it, and the method to do that is gone; `subst` now always works on bound variables. (This changes how the types of generic functions print; previously, you'd get something like `fn identity<i32>(T) -> T`, but now we display the substituted signature `fn identity<i32>(i32) -> i32`, which I think makes more sense.) - once we do this, it's more natural to represent `Param` by a globally unique ID; the use of indices was mostly to make substituting easier. This also means we fix the bug where `Param` loses its name when going through Chalk. - I would actually like to rename `Param` to `Placeholder` to better reflect its use and get closer to Chalk, but I'll leave that to a follow-up. - introduce a context for type lowering, to allow lowering `impl Trait` to different things depending on where we are. And since we have that, we can also lower type parameters directly to variables instead of placeholders. Also, we'll be able to use this later to collect diagnostics. - implement argument position impl trait by lowering it to type parameters. I've realized that this is necessary to correctly implement it; e.g. consider `fn foo(impl Display) -> impl Something`. It's observable that the return type of e.g. `foo(1u32)` unifies with itself, but doesn't unify with e.g. `foo(1i32)`; so the return type needs to be parameterized by the argument type. This fixes a few bugs as well: - type parameters 'losing' their name when they go through Chalk, as mentioned above (i.e. getting `[missing name]` somewhere) - impl trait not being considered as implementing the super traits (very noticeable for the `db` in RA) - the fact that argument impl trait was only turned into variables when the function got called caused type mismatches when the function was used as a value (fixes a few type mismatches in RA) The one thing I'm not so happy with here is how we're lowering `impl Trait` types to variables; since `TypeRef`s don't have an identity currently, we just count how many of them we have seen while going through the function signature. That's quite fragile though, since we have to do it while desugaring generics and while lowering the type signature, and in the exact same order in both cases. We could consider either giving only `TypeRef::ImplTrait` a local id, or maybe just giving all `TypeRef`s an identity after all (we talked about this before)... Follow-up tasks: - handle return position impl trait; we basically need to create a variable and some trait obligations for that variable - rename `Param` to `Placeholder` Co-authored-by: Florian Diebold <florian.diebold@freiheit.com> Co-authored-by: Florian Diebold <flodiebold@gmail.com>
2 parents 961a69b + eefe02c commit 01836a0

File tree

24 files changed

+1174
-614
lines changed

24 files changed

+1174
-614
lines changed

crates/ra_assists/src/handlers/add_new.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use format_buf::format;
2-
use hir::InFile;
2+
use hir::{Adt, InFile};
33
use join_to_string::join;
44
use ra_syntax::{
55
ast::{
@@ -135,16 +135,22 @@ fn find_struct_impl(ctx: &AssistCtx, strukt: &ast::StructDef) -> Option<Option<a
135135
})?;
136136
let mut sb = ctx.source_binder();
137137

138-
let struct_ty = {
138+
let struct_def = {
139139
let src = InFile { file_id: ctx.frange.file_id.into(), value: strukt.clone() };
140-
sb.to_def(src)?.ty(db)
140+
sb.to_def(src)?
141141
};
142142

143143
let block = module.descendants().filter_map(ast::ImplBlock::cast).find_map(|impl_blk| {
144144
let src = InFile { file_id: ctx.frange.file_id.into(), value: impl_blk.clone() };
145145
let blk = sb.to_def(src)?;
146146

147-
let same_ty = blk.target_ty(db) == struct_ty;
147+
// FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}`
148+
// (we currently use the wrong type parameter)
149+
// also we wouldn't want to use e.g. `impl S<u32>`
150+
let same_ty = match blk.target_ty(db).as_adt() {
151+
Some(def) => def == Adt::Struct(struct_def),
152+
None => false,
153+
};
148154
let not_trait_impl = blk.target_trait(db).is_none();
149155

150156
if !(same_ty && not_trait_impl) {

crates/ra_hir/src/code_model.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ use hir_def::{
1010
per_ns::PerNs,
1111
resolver::HasResolver,
1212
type_ref::{Mutability, TypeRef},
13-
AdtId, ConstId, DefWithBodyId, EnumId, FunctionId, HasModule, ImplId, LocalEnumVariantId,
14-
LocalModuleId, LocalStructFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
15-
TypeParamId, UnionId,
13+
AdtId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId,
14+
LocalEnumVariantId, LocalModuleId, LocalStructFieldId, Lookup, ModuleId, StaticId, StructId,
15+
TraitId, TypeAliasId, TypeParamId, UnionId,
1616
};
1717
use hir_expand::{
1818
diagnostics::DiagnosticSink,
@@ -21,7 +21,7 @@ use hir_expand::{
2121
};
2222
use hir_ty::{
2323
autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy,
24-
Canonical, InEnvironment, TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk,
24+
Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor,
2525
};
2626
use ra_db::{CrateId, Edition, FileId};
2727
use ra_prof::profile;
@@ -270,7 +270,13 @@ impl StructField {
270270

271271
pub fn ty(&self, db: &impl HirDatabase) -> Type {
272272
let var_id = self.parent.into();
273-
let ty = db.field_types(var_id)[self.id].clone();
273+
let generic_def_id: GenericDefId = match self.parent {
274+
VariantDef::Struct(it) => it.id.into(),
275+
VariantDef::Union(it) => it.id.into(),
276+
VariantDef::EnumVariant(it) => it.parent.id.into(),
277+
};
278+
let substs = Substs::type_params(db, generic_def_id);
279+
let ty = db.field_types(var_id)[self.id].clone().subst(&substs);
274280
Type::new(db, self.parent.module(db).id.krate.into(), var_id, ty)
275281
}
276282

@@ -755,7 +761,7 @@ pub struct TypeParam {
755761
impl TypeParam {
756762
pub fn name(self, db: &impl HirDatabase) -> Name {
757763
let params = db.generic_params(self.id.parent);
758-
params.types[self.id.local_id].name.clone()
764+
params.types[self.id.local_id].name.clone().unwrap_or_else(Name::missing)
759765
}
760766

761767
pub fn module(self, db: &impl HirDatabase) -> Module {
@@ -789,8 +795,9 @@ impl ImplBlock {
789795
pub fn target_ty(&self, db: &impl HirDatabase) -> Type {
790796
let impl_data = db.impl_data(self.id);
791797
let resolver = self.id.resolver(db);
798+
let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
792799
let environment = TraitEnvironment::lower(db, &resolver);
793-
let ty = Ty::from_hir(db, &resolver, &impl_data.target_type);
800+
let ty = Ty::from_hir(&ctx, &impl_data.target_type);
794801
Type {
795802
krate: self.id.lookup(db).container.module(db).krate,
796803
ty: InEnvironment { value: ty, environment },
@@ -851,9 +858,10 @@ impl Type {
851858
fn from_def(
852859
db: &impl HirDatabase,
853860
krate: CrateId,
854-
def: impl HasResolver + Into<TyDefId>,
861+
def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>,
855862
) -> Type {
856-
let ty = db.ty(def.into());
863+
let substs = Substs::type_params(db, def);
864+
let ty = db.ty(def.into()).subst(&substs);
857865
Type::new(db, krate, def, ty)
858866
}
859867

@@ -950,7 +958,7 @@ impl Type {
950958
match a_ty.ctor {
951959
TypeCtor::Tuple { .. } => {
952960
for ty in a_ty.parameters.iter() {
953-
let ty = ty.clone().subst(&a_ty.parameters);
961+
let ty = ty.clone();
954962
res.push(self.derived(ty));
955963
}
956964
}

crates/ra_hir/src/source_analyzer.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ impl SourceAnalyzer {
178178
}
179179
}
180180

181+
fn trait_env(&self, db: &impl HirDatabase) -> Arc<TraitEnvironment> {
182+
TraitEnvironment::lower(db, &self.resolver)
183+
}
184+
181185
pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> {
182186
let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) {
183187
self.body_source_map.as_ref()?.node_expr(expr.as_ref())?
@@ -186,14 +190,14 @@ impl SourceAnalyzer {
186190
};
187191

188192
let ty = self.infer.as_ref()?[expr_id].clone();
189-
let environment = TraitEnvironment::lower(db, &self.resolver);
193+
let environment = self.trait_env(db);
190194
Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } })
191195
}
192196

193197
pub fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> {
194198
let pat_id = self.pat_id(pat)?;
195199
let ty = self.infer.as_ref()?[pat_id].clone();
196-
let environment = TraitEnvironment::lower(db, &self.resolver);
200+
let environment = self.trait_env(db);
197201
Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } })
198202
}
199203

crates/ra_hir_def/src/generics.rs

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,16 @@ use crate::{
2727
/// Data about a generic parameter (to a function, struct, impl, ...).
2828
#[derive(Clone, PartialEq, Eq, Debug)]
2929
pub struct TypeParamData {
30-
pub name: Name,
30+
pub name: Option<Name>,
3131
pub default: Option<TypeRef>,
32+
pub provenance: TypeParamProvenance,
33+
}
34+
35+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
36+
pub enum TypeParamProvenance {
37+
TypeParamList,
38+
TraitSelf,
39+
ArgumentImplTrait,
3240
}
3341

3442
/// Data about the generic parameters of a function, struct, impl, etc.
@@ -45,10 +53,17 @@ pub struct GenericParams {
4553
/// associated type bindings like `Iterator<Item = u32>`.
4654
#[derive(Clone, PartialEq, Eq, Debug)]
4755
pub struct WherePredicate {
48-
pub type_ref: TypeRef,
56+
pub target: WherePredicateTarget,
4957
pub bound: TypeBound,
5058
}
5159

60+
#[derive(Clone, PartialEq, Eq, Debug)]
61+
pub enum WherePredicateTarget {
62+
TypeRef(TypeRef),
63+
/// For desugared where predicates that can directly refer to a type param.
64+
TypeParam(LocalTypeParamId),
65+
}
66+
5267
type SourceMap = ArenaMap<LocalTypeParamId, Either<ast::TraitDef, ast::TypeParam>>;
5368

5469
impl GenericParams {
@@ -68,6 +83,11 @@ impl GenericParams {
6883
GenericDefId::FunctionId(it) => {
6984
let src = it.lookup(db).source(db);
7085
generics.fill(&mut sm, &src.value);
86+
// lower `impl Trait` in arguments
87+
let data = db.function_data(it);
88+
for param in &data.params {
89+
generics.fill_implicit_impl_trait_args(param);
90+
}
7191
src.file_id
7292
}
7393
GenericDefId::AdtId(AdtId::StructId(it)) => {
@@ -89,8 +109,11 @@ impl GenericParams {
89109
let src = it.lookup(db).source(db);
90110

91111
// traits get the Self type as an implicit first type parameter
92-
let self_param_id =
93-
generics.types.alloc(TypeParamData { name: name![Self], default: None });
112+
let self_param_id = generics.types.alloc(TypeParamData {
113+
name: Some(name![Self]),
114+
default: None,
115+
provenance: TypeParamProvenance::TraitSelf,
116+
});
94117
sm.insert(self_param_id, Either::Left(src.value.clone()));
95118
// add super traits as bounds on Self
96119
// i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
@@ -142,7 +165,11 @@ impl GenericParams {
142165
let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
143166
// FIXME: Use `Path::from_src`
144167
let default = type_param.default_type().map(TypeRef::from_ast);
145-
let param = TypeParamData { name: name.clone(), default };
168+
let param = TypeParamData {
169+
name: Some(name.clone()),
170+
default,
171+
provenance: TypeParamProvenance::TypeParamList,
172+
};
146173
let param_id = self.types.alloc(param);
147174
sm.insert(param_id, Either::Right(type_param.clone()));
148175

@@ -170,11 +197,43 @@ impl GenericParams {
170197
return;
171198
}
172199
let bound = TypeBound::from_ast(bound);
173-
self.where_predicates.push(WherePredicate { type_ref, bound });
200+
self.where_predicates
201+
.push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound });
202+
}
203+
204+
fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) {
205+
type_ref.walk(&mut |type_ref| {
206+
if let TypeRef::ImplTrait(bounds) = type_ref {
207+
let param = TypeParamData {
208+
name: None,
209+
default: None,
210+
provenance: TypeParamProvenance::ArgumentImplTrait,
211+
};
212+
let param_id = self.types.alloc(param);
213+
for bound in bounds {
214+
self.where_predicates.push(WherePredicate {
215+
target: WherePredicateTarget::TypeParam(param_id),
216+
bound: bound.clone(),
217+
});
218+
}
219+
}
220+
});
174221
}
175222

176223
pub fn find_by_name(&self, name: &Name) -> Option<LocalTypeParamId> {
177-
self.types.iter().find_map(|(id, p)| if &p.name == name { Some(id) } else { None })
224+
self.types
225+
.iter()
226+
.find_map(|(id, p)| if p.name.as_ref() == Some(name) { Some(id) } else { None })
227+
}
228+
229+
pub fn find_trait_self_param(&self) -> Option<LocalTypeParamId> {
230+
self.types.iter().find_map(|(id, p)| {
231+
if p.provenance == TypeParamProvenance::TraitSelf {
232+
Some(id)
233+
} else {
234+
None
235+
}
236+
})
178237
}
179238
}
180239

crates/ra_hir_def/src/resolver.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -490,10 +490,12 @@ impl Scope {
490490
}
491491
Scope::GenericParams { params, def } => {
492492
for (local_id, param) in params.types.iter() {
493-
f(
494-
param.name.clone(),
495-
ScopeDef::GenericParam(TypeParamId { local_id, parent: *def }),
496-
)
493+
if let Some(name) = &param.name {
494+
f(
495+
name.clone(),
496+
ScopeDef::GenericParam(TypeParamId { local_id, parent: *def }),
497+
)
498+
}
497499
}
498500
}
499501
Scope::ImplBlockScope(i) => {

crates/ra_hir_def/src/type_ref.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,48 @@ impl TypeRef {
124124
pub(crate) fn unit() -> TypeRef {
125125
TypeRef::Tuple(Vec::new())
126126
}
127+
128+
pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) {
129+
go(self, f);
130+
131+
fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
132+
f(type_ref);
133+
match type_ref {
134+
TypeRef::Fn(types) | TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)),
135+
TypeRef::RawPtr(type_ref, _)
136+
| TypeRef::Reference(type_ref, _)
137+
| TypeRef::Array(type_ref)
138+
| TypeRef::Slice(type_ref) => go(&type_ref, f),
139+
TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
140+
for bound in bounds {
141+
match bound {
142+
TypeBound::Path(path) => go_path(path, f),
143+
TypeBound::Error => (),
144+
}
145+
}
146+
}
147+
TypeRef::Path(path) => go_path(path, f),
148+
TypeRef::Never | TypeRef::Placeholder | TypeRef::Error => {}
149+
};
150+
}
151+
152+
fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
153+
if let Some(type_ref) = path.type_anchor() {
154+
go(type_ref, f);
155+
}
156+
for segment in path.segments().iter() {
157+
if let Some(args_and_bindings) = segment.args_and_bindings {
158+
for arg in &args_and_bindings.args {
159+
let crate::path::GenericArg::Type(type_ref) = arg;
160+
go(type_ref, f);
161+
}
162+
for (_, type_ref) in &args_and_bindings.bindings {
163+
go(type_ref, f);
164+
}
165+
}
166+
}
167+
}
168+
}
127169
}
128170

129171
pub(crate) fn type_bounds_from_ast(type_bounds_opt: Option<ast::TypeBoundList>) -> Vec<TypeBound> {

0 commit comments

Comments
 (0)