Skip to content

Commit

Permalink
WIP: handle ownership for placeholders
Browse files Browse the repository at this point in the history
  • Loading branch information
yorickpeterse committed Dec 7, 2023
1 parent 1b61961 commit 5ddc142
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 42 deletions.
75 changes: 61 additions & 14 deletions types/src/check.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
Arguments, ClassInstance, Database, ForeignType, MethodId, TraitInstance,
TypeArguments, TypeBounds, TypeId, TypeParameterId, TypePlaceholderId,
TypeRef, FLOAT_ID, INT_ID,
Arguments, ClassInstance, Database, ForeignType, MethodId,
PlaceholderOwnership, TraitInstance, TypeArguments, TypeBounds, TypeId,
TypeParameterId, TypePlaceholderId, TypeRef, FLOAT_ID, INT_ID,
};
use std::collections::HashSet;

Expand Down Expand Up @@ -326,15 +326,33 @@ impl<'a> TypeChecker<'a> {
TypeRef::Uni(right_id) if left.is_value_type(self.db) => {
self.check_type_id(left_id, right_id, env, rules)
}
TypeRef::Placeholder(id) => self
.check_type_id_with_placeholder(
TypeRef::Placeholder(id) => {
// TODO: somehow deduplicate this?
// TODO: apply to other cases (e.g. Uni)
match id.ownership {
PlaceholderOwnership::Any
| PlaceholderOwnership::Owned => {}

PlaceholderOwnership::Ref
| PlaceholderOwnership::Mut
if left.is_value_type(self.db)
|| implicit_root_ref => {}

PlaceholderOwnership::Uni
if left.is_value_type(self.db) => {}

_ => return false,
}

self.check_type_id_with_placeholder(
left,
left_id,
original_right,
id,
env,
rules,
),
)
}
TypeRef::Pointer(_) if rules.type_cast => match left_id {
TypeId::ClassInstance(ins) => ins.instance_of().0 == INT_ID,
TypeId::Foreign(ForeignType::Int(_, _)) => true,
Expand Down Expand Up @@ -378,8 +396,10 @@ impl<'a> TypeChecker<'a> {
self.check_type_id(left_id, right_id, env, rules)
}
TypeRef::Placeholder(id) => {
if id.owned {
return false;
match id.ownership {
PlaceholderOwnership::Any
| PlaceholderOwnership::Ref => {}
_ => return false,
}

self.check_type_id_with_placeholder(
Expand Down Expand Up @@ -414,8 +434,11 @@ impl<'a> TypeChecker<'a> {
self.check_type_id(left_id, right_id, env, rules)
}
TypeRef::Placeholder(id) => {
if id.owned && !left.is_value_type(self.db) {
return false;
match id.ownership {
PlaceholderOwnership::Any
| PlaceholderOwnership::Ref => {}
_ if left.is_value_type(self.db) => {}
_ => return false,
}

if let Some(req) = id.required(self.db) {
Expand Down Expand Up @@ -457,8 +480,12 @@ impl<'a> TypeChecker<'a> {
self.check_type_id(left_id, right_id, env, rules)
}
TypeRef::Placeholder(id) => {
if id.owned && !left.is_value_type(self.db) {
return false;
match id.ownership {
PlaceholderOwnership::Any
| PlaceholderOwnership::Ref
| PlaceholderOwnership::Mut => {}
_ if left.is_value_type(self.db) => {}
_ => return false,
}

self.check_type_id_with_placeholder(
Expand Down Expand Up @@ -488,6 +515,7 @@ impl<'a> TypeChecker<'a> {
_ => false,
},
TypeRef::Placeholder(left_id) => {
// TODO: handle ownership
// If we reach this point it means the placeholder isn't
// assigned a value.
left_id.assign(self.db, right);
Expand All @@ -505,8 +533,9 @@ impl<'a> TypeChecker<'a> {
rules.type_cast && ins.instance_of().0 == INT_ID
}
TypeRef::Placeholder(right_id) => {
if right_id.owned {
return false;
match right_id.ownership {
PlaceholderOwnership::Any => {}
_ => return false,
}

self.check_type_id_with_placeholder(
Expand Down Expand Up @@ -1737,6 +1766,24 @@ mod tests {
assert_eq!(var.value(&db), Some(TypeRef::int()));
}

#[test]
fn test_placeholder_with_ownership() {
let mut db = Database::new();
let thing = new_class(&mut db, "Thing");
let any_var = TypePlaceholder::alloc(&mut db, None);
let owned_var = TypePlaceholder::alloc(&mut db, None).as_owned();
let ref_var = TypePlaceholder::alloc(&mut db, None).as_ref();
let mut_var = TypePlaceholder::alloc(&mut db, None).as_mut();
let uni_var = TypePlaceholder::alloc(&mut db, None).as_uni();

check_ok(&db, owned(instance(thing)), placeholder(any_var));
check_ok(&db, owned(instance(thing)), placeholder(owned_var));

check_err(&db, owned(instance(thing)), placeholder(ref_var));
check_err(&db, owned(instance(thing)), placeholder(mut_var));
check_err(&db, owned(instance(thing)), placeholder(uni_var));
}

#[test]
fn test_pointer_with_placeholder() {
let mut db = Database::new();
Expand Down
30 changes: 16 additions & 14 deletions types/src/format.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! Formatting of types.
use crate::{
Arguments, ClassId, ClassInstance, ClassKind, ClosureId, Database,
ForeignType, MethodId, MethodKind, ModuleId, TraitId, TraitInstance,
TypeArguments, TypeId, TypeParameterId, TypePlaceholderId, TypeRef,
Visibility,
ForeignType, MethodId, MethodKind, ModuleId, PlaceholderOwnership, TraitId,
TraitInstance, TypeArguments, TypeId, TypeParameterId, TypePlaceholderId,
TypeRef, Visibility,
};

const MAX_FORMATTING_DEPTH: usize = 8;
Expand Down Expand Up @@ -146,17 +146,7 @@ impl<'a> TypeFormatter<'a> {
Some(TypeRef::Placeholder(id))
if id.value(self.db).is_none() =>
{
// Placeholders without values aren't useful to show to the
// developer, so we show the type parameter instead.
//
// The parameter itself may be assigned a value through the
// type context (e.g. when a type is nested such as
// `Array[Array[T]]`), and we don't want to display that
// assignment as it's only to be used for the outer most
// type. As such, we don't use format_type() here.
format_type_parameter_without_argument(
param, self, id.owned,
);
id.format_type(self);
}
Some(typ) => typ.format_type(self),
_ => param.format_type(self),
Expand Down Expand Up @@ -214,6 +204,18 @@ impl FormatType for TypePlaceholderId {
if let Some(value) = self.value(buffer.db) {
value.format_type(buffer);
} else if let Some(req) = self.required(buffer.db) {
let ownership = match self.ownership {
PlaceholderOwnership::Any => "",
PlaceholderOwnership::Owned => "move ",
PlaceholderOwnership::Uni => "uni ",
PlaceholderOwnership::Ref => "ref ",
PlaceholderOwnership::Mut => "mut ",
};

if !ownership.is_empty() {
buffer.write_ownership(ownership);
}

req.format_type(buffer);
} else {
buffer.write("?");
Expand Down
73 changes: 62 additions & 11 deletions types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,27 +161,64 @@ impl TypePlaceholder {
TypePlaceholder { value: Cell::new(TypeRef::Unknown), required };

db.type_placeholders.push(typ);
TypePlaceholderId { id, owned: false }
TypePlaceholderId { id, ownership: PlaceholderOwnership::Any }
}
}

/// The ownership of a placeholder.
///
/// This only covers ownerships that can be passed around, i.e `uni ref` and
/// `uni mut` aren't handled because you can't pass those values around.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(u8)]
enum PlaceholderOwnership {
Any,
Owned,
Uni,
Ref,
Mut,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct TypePlaceholderId {
id: u32,

/// A flag that indicates a value must be owned for it to be compatible with
/// this placeholder.
/// The ownership values must have before they can be assigned to the
/// placeholder.
///
/// This is stored in the ID/reference as in various instances type
/// placeholders are created ahead of time, at which point we do not yet
/// know if a value must be owned or not. By storing this in the ID we can
/// adjust it accordingly where necessary.
owned: bool,
/// know the desired ownership. In addition, based on how a type parameter
/// is used its ownership may be inferred after it's created.
///
/// By storing this in the ID we can adjust it accordingly where necessary.
/// When resolving placeholder values, their ownership is adjusted according
/// to the ownership of the placeholder.
ownership: PlaceholderOwnership,
}

impl TypePlaceholderId {
pub fn as_owned(self) -> TypePlaceholderId {
TypePlaceholderId { id: self.id, owned: true }
fn with_ownership(
self,
ownership: PlaceholderOwnership,
) -> TypePlaceholderId {
TypePlaceholderId { id: self.id, ownership }
}

fn as_owned(self) -> TypePlaceholderId {
self.with_ownership(PlaceholderOwnership::Owned)
}

fn as_uni(self) -> TypePlaceholderId {
self.with_ownership(PlaceholderOwnership::Uni)
}

fn as_ref(self) -> TypePlaceholderId {
self.with_ownership(PlaceholderOwnership::Ref)
}

fn as_mut(self) -> TypePlaceholderId {
self.with_ownership(PlaceholderOwnership::Mut)
}

pub fn value(self, db: &Database) -> Option<TypeRef> {
Expand All @@ -194,7 +231,17 @@ impl TypePlaceholderId {
match typ {
TypeRef::Placeholder(id) => id.value(db),
TypeRef::Unknown => None,
_ => Some(typ),
_ => {
let res = match self.ownership {
PlaceholderOwnership::Any => typ,
PlaceholderOwnership::Owned => typ.as_owned(db),
PlaceholderOwnership::Uni => typ.as_uni(db),
PlaceholderOwnership::Ref => typ.as_ref(db),
PlaceholderOwnership::Mut => typ.force_as_mut(db),
};

Some(res)
}
}
}

Expand Down Expand Up @@ -5361,8 +5408,12 @@ mod tests {

#[test]
fn test_type_placeholder_id_as_owned() {
let id = TypePlaceholderId { id: 1, owned: false };
let id =
TypePlaceholderId { id: 1, ownership: PlaceholderOwnership::Any };

assert_eq!(id.as_owned(), TypePlaceholderId { id: 1, owned: true });
assert_eq!(
id.as_owned(),
TypePlaceholderId { id: 1, ownership: PlaceholderOwnership::Owned }
);
}
}
22 changes: 19 additions & 3 deletions types/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ impl<'a> TypeResolver<'a> {
},
TypeRef::Ref(id) => match self.resolve_type_id(id) {
Either::Left(res) => TypeRef::Ref(res),
Either::Right(TypeRef::Placeholder(id)) => {
TypeRef::Placeholder(id.as_ref())
}
Either::Right(
TypeRef::Owned(typ) | TypeRef::Any(typ) | TypeRef::Mut(typ),
) => TypeRef::Ref(typ),
Expand All @@ -143,6 +146,9 @@ impl<'a> TypeResolver<'a> {
},
TypeRef::Mut(id) => match self.resolve_type_id(id) {
Either::Left(res) => TypeRef::Mut(res),
Either::Right(TypeRef::Placeholder(id)) => {
TypeRef::Placeholder(id.as_mut())
}
Either::Right(TypeRef::Owned(typ) | TypeRef::Any(typ)) => {
TypeRef::Mut(typ)
}
Expand All @@ -151,6 +157,9 @@ impl<'a> TypeResolver<'a> {
},
TypeRef::Uni(id) => match self.resolve_type_id(id) {
Either::Left(res) => TypeRef::Uni(res),
Either::Right(TypeRef::Placeholder(id)) => {
TypeRef::Placeholder(id.as_uni())
}
Either::Right(TypeRef::Owned(typ) | TypeRef::Any(typ)) => {
TypeRef::Uni(typ)
}
Expand Down Expand Up @@ -298,7 +307,10 @@ mod tests {
mutable_uni, new_parameter, new_trait, owned, parameter, placeholder,
pointer, rigid, type_arguments, type_bounds, uni,
};
use crate::{Block, ClassId, Closure, TypePlaceholder, TypePlaceholderId};
use crate::{
Block, ClassId, Closure, PlaceholderOwnership, TypePlaceholder,
TypePlaceholderId,
};

fn resolve(
db: &mut Database,
Expand Down Expand Up @@ -849,11 +861,15 @@ mod tests {

assert_eq!(
resolve(&mut db, &args, &bounds, owned(parameter(param))),
placeholder(TypePlaceholderId { id: 0, owned: true })
placeholder(TypePlaceholderId {
id: 0,
ownership: PlaceholderOwnership::Owned
})
);

assert_eq!(
TypePlaceholderId { id: 0, owned: false }.required(&db),
TypePlaceholderId { id: 0, ownership: PlaceholderOwnership::Any }
.required(&db),
Some(bound)
);
}
Expand Down

0 comments on commit 5ddc142

Please sign in to comment.