Skip to content

Commit

Permalink
Fix resolving nested types as immutable
Browse files Browse the repository at this point in the history
When resloving a nested type (e.g. `Array[Array[Int]]`) as immutable,
the immutable ownership is only applied to the outer-most type, instead
of every type. This means the result is now e.g.
`ref Array[Array[Int]]`, instead of `ref Array[ref Array[ref Int]]`.

This fixes #551.

Changelog: fixed
  • Loading branch information
yorickpeterse committed May 27, 2023
1 parent f8bd249 commit 227429b
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 49 deletions.
2 changes: 1 addition & 1 deletion types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3803,7 +3803,7 @@ impl TypeRef {
TypeRef::Owned(id) | TypeRef::Infer(id) | TypeRef::Mut(id) => {
TypeRef::Ref(id)
}
TypeRef::Uni(id) => TypeRef::UniRef(id),
TypeRef::Uni(id) | TypeRef::UniMut(id) => TypeRef::UniRef(id),
TypeRef::Placeholder(id) => {
id.value(db).map_or(self, |v| v.as_ref(db))
}
Expand Down
101 changes: 53 additions & 48 deletions types/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub struct TypeResolver<'a> {
/// is produced when resolving the parameter.
bounds: &'a TypeBounds,

/// If resolved types should be made immutable or not.
/// If the resolved type should be made immutable or not.
immutable: bool,

/// When set to true, non-rigid type parameters are turned into rigid
Expand Down Expand Up @@ -72,6 +72,16 @@ impl<'a> TypeResolver<'a> {
}

pub fn resolve(&mut self, value: TypeRef) -> TypeRef {
let typ = self.resolve_type_ref(value);

if self.immutable {
typ.as_ref(self.db)
} else {
typ
}
}

pub fn resolve_type_ref(&mut self, value: TypeRef) -> TypeRef {
if let Some(&cached) = self.cached.get(&value) {
return cached;
}
Expand All @@ -86,7 +96,7 @@ impl<'a> TypeResolver<'a> {
let resolved = match value {
TypeRef::Owned(id) | TypeRef::Infer(id) => {
match self.resolve_type_id(id) {
Either::Left(res) => self.owned(res),
Either::Left(res) => TypeRef::Owned(res),
Either::Right(typ) => typ,
}
}
Expand All @@ -101,30 +111,31 @@ impl<'a> TypeResolver<'a> {
Either::Right(typ) => typ,
},
TypeRef::Mut(id) => match self.resolve_type_id(id) {
Either::Left(res) => self.mutable(res),
Either::Right(TypeRef::Owned(typ)) => self.mutable(typ),
Either::Right(TypeRef::Uni(typ)) => self.mutable_uni(typ),
Either::Left(res) => TypeRef::Mut(res),
Either::Right(TypeRef::Owned(typ)) => TypeRef::Mut(typ),
Either::Right(TypeRef::Uni(typ)) => TypeRef::UniMut(typ),
Either::Right(typ) => typ,
},
TypeRef::Uni(id) => match self.resolve_type_id(id) {
Either::Left(res) => self.uni(res),
Either::Right(TypeRef::Owned(typ)) => self.uni(typ),
Either::Left(res) => TypeRef::Uni(res),
Either::Right(TypeRef::Owned(typ)) => TypeRef::Uni(typ),
Either::Right(typ) => typ,
},
TypeRef::UniRef(id) => match self.resolve_type_id(id) {
Either::Left(res) => TypeRef::UniRef(res),
Either::Right(typ) => typ,
},
TypeRef::UniMut(id) => match self.resolve_type_id(id) {
Either::Left(res) => self.mutable_uni(res),
Either::Left(res) => TypeRef::UniMut(res),
Either::Right(typ) => typ,
},
// If a placeholder is unassigned we need to return it as-is. This
// way future use of the placeholder allows us to infer the current
// type.
TypeRef::Placeholder(id) => {
id.value(self.db).map(|v| self.resolve(v)).unwrap_or(value)
}
TypeRef::Placeholder(id) => id
.value(self.db)
.map(|v| self.resolve_type_ref(v))
.unwrap_or(value),
_ => value,
};

Expand Down Expand Up @@ -195,10 +206,10 @@ impl<'a> TypeResolver<'a> {
self.immutable = false;

for arg in new.arguments.mapping.values_mut() {
arg.value_type = self.resolve(arg.value_type);
arg.value_type = self.resolve_type_ref(arg.value_type);
}

new.return_type = self.resolve(new.return_type);
new.return_type = self.resolve_type_ref(new.return_type);
self.immutable = immutable;
Either::Left(TypeId::Closure(Closure::add(self.db, new)))
}
Expand All @@ -208,7 +219,7 @@ impl<'a> TypeResolver<'a> {

fn resolve_arguments(&mut self, arguments: &mut TypeArguments) {
for value in arguments.mapping.values_mut() {
*value = self.resolve(*value);
*value = self.resolve_type_ref(*value);
}
}

Expand All @@ -222,7 +233,7 @@ impl<'a> TypeResolver<'a> {
let key = id.original(self.db).unwrap_or(id);

if let Some(arg) = self.type_arguments.get(key) {
return Some(self.resolve(arg));
return Some(self.resolve_type_ref(arg));
}

// Inside a trait we may end up referring to type parameters from a
Expand All @@ -232,7 +243,7 @@ impl<'a> TypeResolver<'a> {
.surrounding_trait
.and_then(|t| t.get(self.db).inherited_type_arguments.get(key))
{
return Some(self.resolve(arg));
return Some(self.resolve_type_ref(arg));
}

None
Expand All @@ -241,38 +252,6 @@ impl<'a> TypeResolver<'a> {
fn remap_type_parameter(&self, id: TypeParameterId) -> TypeParameterId {
self.bounds.get(id).unwrap_or(id)
}

fn owned(&self, id: TypeId) -> TypeRef {
if self.immutable {
TypeRef::Ref(id)
} else {
TypeRef::Owned(id)
}
}

fn uni(&self, id: TypeId) -> TypeRef {
if self.immutable {
TypeRef::UniRef(id)
} else {
TypeRef::Uni(id)
}
}

fn mutable(&self, id: TypeId) -> TypeRef {
if self.immutable {
TypeRef::Ref(id)
} else {
TypeRef::Mut(id)
}
}

fn mutable_uni(&self, id: TypeId) -> TypeRef {
if self.immutable {
TypeRef::UniRef(id)
} else {
TypeRef::UniMut(id)
}
}
}

#[cfg(test)]
Expand Down Expand Up @@ -324,6 +303,32 @@ mod tests {
);
}

#[test]
fn test_immutable_nested_type() {
let mut db = Database::new();
let array = ClassId::array();
let int = ClassId::int();

array.new_type_parameter(&mut db, "T".to_string());

let int_array = owned(generic_instance_id(
&mut db,
array,
vec![owned(instance(int))],
));

let input = owned(generic_instance_id(&mut db, array, vec![int_array]));
let resolved = resolve_immutable(
&mut db,
&TypeArguments::new(),
&TypeBounds::new(),
input,
);

assert!(resolved.is_ref(&db));
assert!(resolved.type_arguments(&db).pairs()[0].1.is_owned(&db));
}

#[test]
fn test_infer() {
let mut db = Database::new();
Expand Down

0 comments on commit 227429b

Please sign in to comment.