Skip to content

Commit

Permalink
WIP: add support for the move type modifier
Browse files Browse the repository at this point in the history
  • Loading branch information
yorickpeterse committed Nov 28, 2023
1 parent aa615ed commit 0933d57
Show file tree
Hide file tree
Showing 12 changed files with 345 additions and 172 deletions.
2 changes: 2 additions & 0 deletions ast/src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,7 @@ pub enum Type {
Ref(Box<ReferenceType>),
Mut(Box<ReferenceType>),
Uni(Box<ReferenceType>),
Owned(Box<ReferenceType>),
Closure(Box<ClosureType>),
Tuple(Box<TupleType>),
}
Expand All @@ -983,6 +984,7 @@ impl Node for Type {
Type::Ref(ref typ) => typ.location(),
Type::Mut(ref typ) => typ.location(),
Type::Uni(ref typ) => typ.location(),
Type::Owned(ref typ) => typ.location(),
Type::Closure(ref typ) => typ.location(),
Type::Tuple(ref typ) => typ.location(),
}
Expand Down
25 changes: 25 additions & 0 deletions ast/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,9 @@ impl Parser {
TokenKind::Fn => Type::Closure(Box::new(self.closure_type(start)?)),
TokenKind::Ref => Type::Ref(Box::new(self.reference_type(start)?)),
TokenKind::Mut => Type::Mut(Box::new(self.reference_type(start)?)),
TokenKind::Move => {
Type::Owned(Box::new(self.reference_type(start)?))
}
TokenKind::Uni => Type::Uni(Box::new(self.reference_type(start)?)),
TokenKind::ParenOpen => {
Type::Tuple(Box::new(self.tuple_type(start)?))
Expand Down Expand Up @@ -3879,6 +3882,28 @@ mod tests {
);
}

#[test]
fn test_type_reference_with_owned_reference_type() {
let mut parser = parser("move A");
let start = parser.require().unwrap();

assert_eq!(
parser.type_reference(start).unwrap(),
Type::Owned(Box::new(ReferenceType {
type_reference: ReferrableType::Named(Box::new(TypeName {
name: Constant {
source: None,
name: "A".to_string(),
location: cols(6, 6),
},
arguments: None,
location: cols(6, 6)
})),
location: cols(1, 6)
}))
);
}

#[test]
fn test_type_reference_with_double_reference() {
let mut parser = parser("ref ref A");
Expand Down
25 changes: 25 additions & 0 deletions compiler/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,7 @@ pub(crate) enum Type {
Ref(Box<ReferenceType>),
Mut(Box<ReferenceType>),
Uni(Box<ReferenceType>),
Owned(Box<ReferenceType>),
Closure(Box<ClosureType>),
Tuple(Box<TupleType>),
}
Expand All @@ -669,6 +670,7 @@ impl Type {
Type::Ref(ref node) => &node.location,
Type::Mut(ref node) => &node.location,
Type::Uni(ref node) => &node.location,
Type::Owned(ref node) => &node.location,
Type::Closure(ref node) => &node.location,
Type::Tuple(ref node) => &node.location,
}
Expand Down Expand Up @@ -1542,6 +1544,7 @@ impl<'a> LowerToHir<'a> {
}
ast::Type::Ref(node) => Type::Ref(self.reference_type(*node)),
ast::Type::Mut(node) => Type::Mut(self.reference_type(*node)),
ast::Type::Owned(node) => Type::Owned(self.reference_type(*node)),
ast::Type::Uni(node) => Type::Uni(self.reference_type(*node)),
ast::Type::Closure(node) => Type::Closure(self.closure_type(*node)),
ast::Type::Tuple(node) => Type::Tuple(self.tuple_type(*node)),
Expand Down Expand Up @@ -3330,6 +3333,28 @@ mod tests {
);
}

#[test]
fn test_lower_owned_reference_type() {
let hir = lower_type("move B");

assert_eq!(
hir,
Type::Owned(Box::new(ReferenceType {
type_reference: ReferrableType::Named(Box::new(TypeName {
source: None,
resolved_type: types::TypeRef::Unknown,
name: Constant {
name: "B".to_string(),
location: cols(14, 14)
},
arguments: Vec::new(),
location: cols(14, 14)
})),
location: cols(9, 14)
}))
);
}

#[test]
fn test_lower_closure_type() {
let hir = lower_type("fn (A) -> C");
Expand Down
38 changes: 18 additions & 20 deletions compiler/src/type_check/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,10 +286,8 @@ impl MethodCall {
// types, so we need to ensure all references resolve into the same
// types, hence we create type placeholders here.
for param in method.type_parameters(&state.db).into_iter() {
type_arguments.assign(
param,
TypeRef::placeholder(&mut state.db, Some(param)),
);
type_arguments
.assign(param, TypeRef::any_placeholder(&mut state.db, param));
}

// Static methods may use/return type parameters of the surrounding
Expand All @@ -300,7 +298,7 @@ impl MethodCall {
for param in class.type_parameters(&state.db) {
type_arguments.assign(
param,
TypeRef::placeholder(&mut state.db, Some(param)),
TypeRef::any_placeholder(&mut state.db, param),
);
}
}
Expand Down Expand Up @@ -353,7 +351,7 @@ impl MethodCall {
};

// If the receiver is rigid, it may introduce additional type arguments
// through its type parameter requirements. These are typed as Infer(),
// through its type parameter requirements. These are typed as Any(),
// but we want them as rigid types. In addition, we need to take care or
// remapping any bound parameters.
//
Expand All @@ -363,7 +361,7 @@ impl MethodCall {
// handle it here when/if necessary.
if receiver.is_rigid_type_parameter(&state.db) {
for val in type_arguments.values_mut() {
if let TypeRef::Infer(TypeId::TypeParameter(id)) = val {
if let TypeRef::Any(TypeId::TypeParameter(id)) = val {
*val = TypeRef::Owned(TypeId::RigidTypeParameter(
bounds.get(*id).unwrap_or(*id),
));
Expand Down Expand Up @@ -495,6 +493,12 @@ impl MethodCall {
self.type_arguments.clone(),
);

// TODO: remove
if self.method.name(&state.db) == "foo" {
println!("{:?} -> {:?}", given, expected);
println!("args: {:?}", scope.right);
}

if !TypeChecker::new(&state.db)
.check_argument(given, expected, &mut scope)
{
Expand Down Expand Up @@ -2487,7 +2491,7 @@ impl<'a> CheckMethodBody<'a> {

TypeResolver::new(db, targs, bounds).resolve(raw)
})
.unwrap_or_else(|| TypeRef::placeholder(db, None))
.unwrap_or_else(|| TypeRef::placeholder(db))
};

closure.set_return_type(self.db_mut(), return_type);
Expand Down Expand Up @@ -2523,7 +2527,7 @@ impl<'a> CheckMethodBody<'a> {
TypeResolver::new(db, targs, bounds).resolve(t)
})
})
.unwrap_or_else(|| TypeRef::placeholder(db, None))
.unwrap_or_else(|| TypeRef::placeholder(db))
};

let var =
Expand Down Expand Up @@ -3085,7 +3089,7 @@ impl<'a> CheckMethodBody<'a> {
.diagnostics
.throw_not_available(self.file(), node.location.clone()),
ThrowKind::Infer(pid) => {
let var = TypeRef::placeholder(self.db_mut(), None);
let var = TypeRef::placeholder(self.db_mut());
let typ = TypeRef::result_type(self.db_mut(), var, expr);

pid.assign(self.db(), typ);
Expand Down Expand Up @@ -3935,11 +3939,8 @@ impl<'a> CheckMethodBody<'a> {
scope: &mut LexicalScope,
) -> TypeRef {
let expr_type = self.expression(&mut node.value, scope);
let rules = Rules {
type_parameters_as_rigid: true,
type_parameters_as_owned: true,
..Default::default()
};
let rules =
Rules { type_parameters_as_rigid: true, ..Default::default() };

let type_scope = TypeScope::with_bounds(
self.module,
Expand Down Expand Up @@ -4272,11 +4273,8 @@ impl<'a> CheckMethodBody<'a> {
node: &mut hir::Type,
self_type: TypeId,
) -> TypeRef {
let rules = Rules {
type_parameters_as_rigid: true,
type_parameters_as_owned: true,
..Default::default()
};
let rules =
Rules { type_parameters_as_rigid: true, ..Default::default() };
let type_scope = TypeScope::with_bounds(
self.module,
self_type,
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/type_check/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,7 @@ impl<'a> DefineMethods<'a> {
let args = class_id
.type_parameters(self.db())
.into_iter()
.map(|param| TypeRef::Infer(TypeId::TypeParameter(param)))
.map(|param| TypeRef::Any(TypeId::TypeParameter(param)))
.collect();

ClassInstance::with_types(self.db_mut(), class_id, args)
Expand Down
58 changes: 28 additions & 30 deletions compiler/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub(crate) mod methods;

#[derive(Eq, PartialEq)]
enum RefKind {
Default,
Owned,
Ref,
Mut,
Expand All @@ -28,6 +29,12 @@ enum RefKind {
impl RefKind {
fn into_type_ref(self, id: TypeId) -> TypeRef {
match self {
Self::Default => match id {
TypeId::TypeParameter(_) | TypeId::RigidTypeParameter(_) => {
TypeRef::Any(id)
}
_ => TypeRef::Owned(id),
},
Self::Owned => TypeRef::Owned(id),
Self::Ref => TypeRef::Ref(id),
Self::Mut => TypeRef::Mut(id),
Expand Down Expand Up @@ -97,10 +104,6 @@ impl<'a> TypeScope<'a> {
/// Rules to apply when defining and checking the types of type signatures.
#[derive(Copy, Clone)]
pub(crate) struct Rules {
/// When set to `true`, type parameters are defined as owned values; rather
/// than allowing both owned values and references.
pub(crate) type_parameters_as_owned: bool,

/// When set to `true`, type parameters are defined as rigid parameters.
pub(crate) type_parameters_as_rigid: bool,

Expand All @@ -114,7 +117,6 @@ pub(crate) struct Rules {
impl Default for Rules {
fn default() -> Self {
Self {
type_parameters_as_owned: false,
type_parameters_as_rigid: false,
allow_private_types: true,
allow_refs: true,
Expand Down Expand Up @@ -169,7 +171,7 @@ impl<'a> DefineTypeSignature<'a> {
fn define_type(&mut self, node: &mut hir::Type) -> TypeRef {
match node {
hir::Type::Named(ref mut n) => {
self.define_type_name(n, RefKind::Owned)
self.define_type_name(n, RefKind::Default)
}
hir::Type::Ref(_) | hir::Type::Mut(_) if !self.rules.allow_refs => {
self.state.diagnostics.error(
Expand All @@ -189,6 +191,9 @@ impl<'a> DefineTypeSignature<'a> {
hir::Type::Uni(ref mut n) => {
self.define_reference_type(n, RefKind::Uni)
}
hir::Type::Owned(ref mut n) => {
self.define_reference_type(n, RefKind::Owned)
}
hir::Type::Closure(ref mut n) => {
self.define_closure_type(n, RefKind::Owned)
}
Expand Down Expand Up @@ -288,7 +293,7 @@ impl<'a> DefineTypeSignature<'a> {
// handling them first.
match name.as_str() {
"Never" => {
if kind == RefKind::Owned {
if kind == RefKind::Default {
TypeRef::Never
} else {
self.state.diagnostics.error(
Expand Down Expand Up @@ -403,30 +408,22 @@ impl<'a> DefineTypeSignature<'a> {
TypeId::TypeParameter(param_id)
};

match kind {
RefKind::Owned if self.rules.type_parameters_as_owned => {
TypeRef::Owned(type_id)
}
RefKind::Owned => TypeRef::Infer(type_id),
RefKind::Uni => TypeRef::Uni(type_id),
RefKind::Ref => TypeRef::Ref(type_id),
RefKind::Mut => {
if !param_id.is_mutable(self.db()) {
self.state.diagnostics.error(
DiagnosticId::InvalidType,
format!(
"the type 'mut {name}' is invalid, as '{name}' \
if let RefKind::Mut = kind {
if !param_id.is_mutable(self.db()) {
self.state.diagnostics.error(
DiagnosticId::InvalidType,
format!(
"the type 'mut {name}' is invalid, as '{name}' \
might be immutable at runtime",
name = id.name(self.db()),
),
self.file(),
node.location.clone(),
);
}

TypeRef::Mut(type_id)
name = id.name(self.db()),
),
self.file(),
node.location.clone(),
);
}
}

kind.into_type_ref(type_id)
}

fn define_closure_type(
Expand Down Expand Up @@ -623,6 +620,7 @@ impl<'a> CheckTypeSignature<'a> {
hir::Type::Ref(ref n) => self.check_reference_type(n),
hir::Type::Uni(ref n) => self.check_reference_type(n),
hir::Type::Mut(ref n) => self.check_reference_type(n),
hir::Type::Owned(ref n) => self.check_reference_type(n),
hir::Type::Closure(ref n) => self.check_closure_type(n),
hir::Type::Tuple(ref n) => self.check_tuple_type(n),
}
Expand Down Expand Up @@ -730,13 +728,13 @@ impl<'a> CheckTypeSignature<'a> {
) {
let exp_args =
parameters.iter().fold(TypeArguments::new(), |mut args, &p| {
args.assign(p, TypeRef::placeholder(self.db_mut(), Some(p)));
args.assign(p, TypeRef::any_placeholder(self.db_mut(), p));
args
});

for (param, node) in parameters.into_iter().zip(node.arguments.iter()) {
let arg = arguments.get(param).unwrap();
let exp = TypeRef::Infer(TypeId::TypeParameter(param));
let exp = TypeRef::Any(TypeId::TypeParameter(param));
let mut env = Environment::new(
arg.type_arguments(self.db()),
exp_args.clone(),
Expand Down
Loading

0 comments on commit 0933d57

Please sign in to comment.