Skip to content

Commit

Permalink
Add an actual subtyping relation
Browse files Browse the repository at this point in the history
  • Loading branch information
jfecher committed Aug 11, 2024
1 parent d53eddf commit d902bd7
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 249 deletions.
13 changes: 8 additions & 5 deletions src/hir/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ impl std::hash::Hash for DefinitionType {
types::Type::TypeVariable(_) => (), // Do nothing
types::Type::Function(_) => (),
types::Type::TypeApplication(_, _) => (),
types::Type::Ref(shared, mutable, _) => {
shared.hash(state);
mutable.hash(state);
types::Type::Ref { sharedness, mutability, lifetime: _ } => {
sharedness.hash(state);
mutability.hash(state);
},
types::Type::Struct(field_names, _) => {
for name in field_names {
Expand All @@ -77,6 +77,7 @@ impl std::hash::Hash for DefinitionType {
id.hash(state);
}
},
types::Type::Tag(tag) => tag.hash(state),
}
})
}
Expand All @@ -98,8 +99,9 @@ fn definition_type_eq(a: &types::Type, b: &types::Type) -> bool {
// This will monomorphize separate definitions for polymorphically-owned references
// which is undesired. Defaulting them to shared/owned though can change behavior
// if traits are involved.
(Type::Ref(shared1, mutable1, _), Type::Ref(shared2, mutable2, _)) => {
shared1 == shared2 && mutable1 == mutable2
(Type::Ref { sharedness: shared1, mutability: mutable1, .. },
Type::Ref { sharedness: shared2, mutability: mutable2, .. }) => {
definition_type_eq(shared1, shared2) && definition_type_eq(mutable1, mutable2)
},
(Type::Function(f1), Type::Function(f2)) => {
if f1.parameters.len() != f2.parameters.len() {
Expand Down Expand Up @@ -137,6 +139,7 @@ fn definition_type_eq(a: &types::Type, b: &types::Type) -> bool {
id1 == id2 && args1.iter().zip(args2).all(|(t1, t2)| definition_type_eq(t1, t2))
})
},
(Type::Tag(tag1), Type::Tag(tag2)) => tag1 == tag2,
(othera, otherb) => {
assert_ne!(std::mem::discriminant(othera), std::mem::discriminant(otherb), "ICE: Missing match case");
false
Expand Down
28 changes: 20 additions & 8 deletions src/hir/monomorphisation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,12 @@ impl<'c> Context<'c> {

let fuel = fuel - 1;
match &self.cache.type_bindings[id.0] {
Bound(TypeVariable(id2) | Ref(_, _, id2)) => self.find_binding(*id2, fuel),
Bound(TypeVariable(id2)) => self.find_binding(*id2, fuel),
Bound(binding) => Ok(binding),
Unbound(..) => {
for bindings in self.monomorphisation_bindings.iter().rev() {
match bindings.get(&id) {
Some(TypeVariable(id2) | Ref(_, _, id2)) => return self.find_binding(*id2, fuel),
Some(TypeVariable(id2)) => return self.find_binding(*id2, fuel),
Some(binding) => return Ok(binding),
None => (),
}
Expand Down Expand Up @@ -204,7 +204,12 @@ impl<'c> Context<'c> {
let args = fmap(args, |arg| self.follow_all_bindings_inner(arg, fuel));
TypeApplication(Box::new(con), args)
},
Ref(..) => typ.clone(),
Ref { mutability, sharedness, lifetime } => {
let mutability = Box::new(self.follow_all_bindings_inner(mutability, fuel));
let sharedness = Box::new(self.follow_all_bindings_inner(sharedness, fuel));
let lifetime = Box::new(self.follow_all_bindings_inner(lifetime, fuel));
Ref { mutability, sharedness, lifetime }
},
Struct(fields, id) => match self.find_binding(*id, fuel) {
Ok(binding) => self.follow_all_bindings_inner(binding, fuel),
Err(_) => {
Expand All @@ -217,6 +222,7 @@ impl<'c> Context<'c> {
},
},
Effects(effects) => self.follow_all_effect_bindings_inner(effects, fuel),
Tag(tag) => Tag(*tag),
}
}

Expand Down Expand Up @@ -315,6 +321,9 @@ impl<'c> Context<'c> {
Primitive(FloatType) => {
unreachable!("'Float' type constructor without arguments found during size_of_type")
},
Tag(tag) => {
unreachable!("'{}' found during size_of_type", tag)
}

Function(..) => Self::ptr_size(),

Expand Down Expand Up @@ -346,7 +355,7 @@ impl<'c> Context<'c> {
_ => unreachable!("Kind error inside size_of_type"),
},

Ref(..) => Self::ptr_size(),
Ref { .. } => Self::ptr_size(),
Struct(fields, rest) => {
if let Ok(binding) = self.find_binding(*rest, RECURSION_LIMIT) {
let binding = binding.clone();
Expand Down Expand Up @@ -519,7 +528,7 @@ impl<'c> Context<'c> {
let typ = self.follow_bindings_shallow(typ);

match typ {
Ok(Primitive(PrimitiveType::Ptr) | Ref(..)) => Type::Primitive(hir::PrimitiveType::Pointer),
Ok(Primitive(PrimitiveType::Ptr) | Ref { .. }) => Type::Primitive(hir::PrimitiveType::Pointer),
Ok(Primitive(PrimitiveType::IntegerType)) => {
if self.is_type_variable(&args[0]) {
// Default to i32
Expand Down Expand Up @@ -553,11 +562,14 @@ impl<'c> Context<'c> {
}
},

Ref(..) => {
Ref { .. } => {
unreachable!(
"Kind error during monomorphisation. Attempted to translate a `ref` without a type argument"
)
},
Tag(tag) => {
unreachable!("Kind error during monomorphisation. Attempted to translate a `{}` as a type", tag)
}
Struct(fields, rest) => {
if let Ok(binding) = self.find_binding(*rest, fuel) {
let binding = binding.clone();
Expand Down Expand Up @@ -1517,7 +1529,7 @@ impl<'c> Context<'c> {
TypeApplication(typ, args) => {
match typ.as_ref() {
// Pass through ref types transparently
types::Type::Ref(..) => self.get_field_index(field_name, &args[0]),
types::Type::Ref { .. } => self.get_field_index(field_name, &args[0]),
// These last 2 cases are the same. They're duplicated to avoid another follow_bindings_shallow call.
typ => self.get_field_index(field_name, typ),
}
Expand Down Expand Up @@ -1551,7 +1563,7 @@ impl<'c> Context<'c> {
let ref_type = match lhs_type {
types::Type::TypeApplication(constructor, args) => match self.follow_bindings_shallow(constructor.as_ref())
{
Ok(types::Type::Ref(..)) => Some(self.convert_type(&args[0])),
Ok(types::Type::Ref { .. }) => Some(self.convert_type(&args[0])),
_ => None,
},
_ => None,
Expand Down
133 changes: 0 additions & 133 deletions src/lifetimes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,138 +1,5 @@
use crate::cache::ModuleCache;
use crate::parser::ast::Ast;
use crate::types::TypeVariableId;

/// A lifetime variable is represented simply as a type variable for ease of unification
/// during the type inference pass.
pub type LifetimeVariableId = TypeVariableId;

// struct LifetimeAnalyzer {
// pub level: StackFrameIndex,
//
// /// Map from RegionVariableId -> StackFrame
// /// Contains the stack frame index each region should be allocated in
// pub lifetimes: Vec<StackFrameIndex>,
// }
//
// struct StackFrameIndex(usize);

#[allow(unused)]
pub fn infer<'c>(_ast: &mut Ast<'c>, _cache: &mut ModuleCache<'c>) {}

// trait InferableLifetime {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>);
// }
//
// impl<'ast> InferableLifetime for Ast<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
// dispatch_on_expr!(self, InferableLifetime::infer_lifetime, analyzer, cache)
// }
// }
//
// impl<'ast> InferableLifetime for ast::Literal<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
// // Do nothing: literals cannot contain a ref type
// }
// }
//
// impl<'ast> InferableLifetime for ast::Variable<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::Lambda<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::FunctionCall<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::Definition<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::If<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::Match<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::TypeDefinition<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::TypeAnnotation<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::Import<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::TraitDefinition<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::TraitImpl<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::Return<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::Sequence<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::Extern<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::MemberAccess<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::Tuple<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
//
// impl<'ast> InferableLifetime for ast::Assignment<'ast> {
// fn infer_lifetime<'c>(&mut self, analyzer: &mut LifetimeAnalyzer, cache: &mut ModuleCache<'c>) {
//
// }
// }
24 changes: 19 additions & 5 deletions src/nameresolution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use crate::types::traits::ConstraintSignature;
use crate::types::typed::Typed;
use crate::types::{
Field, FunctionType, GeneralizedType, LetBindingLevel, PrimitiveType, Type, TypeConstructor, TypeInfoBody,
TypeInfoId, TypeVariableId, INITIAL_LEVEL, STRING_TYPE,
TypeInfoId, TypeVariableId, INITIAL_LEVEL, STRING_TYPE, TypeTag,
};
use crate::util::{fmap, timing, trustme};

Expand Down Expand Up @@ -589,9 +589,10 @@ impl<'c> NameResolver {
Type::TypeVariable(_) => 0,
Type::UserDefined(id) => cache[*id].args.len(),
Type::TypeApplication(_, _) => 0,
Type::Ref(..) => 1,
Type::Ref { .. } => 1,
Type::Struct(_, _) => 0,
Type::Effects(_) => 0,
Type::Tag(_) => 0,
}
}

Expand Down Expand Up @@ -739,14 +740,27 @@ impl<'c> NameResolver {

Type::TypeApplication(Box::new(pair), args)
},
ast::Type::Reference(sharednes, mutability, _) => {
ast::Type::Reference(sharedness, mutability, _) => {
// When translating ref types, all have a hidden lifetime variable that is unified
// under the hood by the compiler to determine the reference's stack lifetime.
// This is never able to be manually specified by the programmer, so we use
// next_type_variable_id on the cache rather than the NameResolver's version which
// would add a name into scope.
let lifetime_variable = cache.next_type_variable_id(self.let_binding_level);
Type::Ref(*sharednes, *mutability, lifetime_variable)
let lifetime = Box::new(cache.next_type_variable(self.let_binding_level));

let sharedness = Box::new(match sharedness {
ast::Sharedness::Polymorphic => cache.next_type_variable(self.let_binding_level),
ast::Sharedness::Shared => Type::Tag(TypeTag::Shared),
ast::Sharedness::Owned => Type::Tag(TypeTag::Owned),
});

let mutability = Box::new(match mutability {
ast::Mutability::Polymorphic => cache.next_type_variable(self.let_binding_level),
ast::Mutability::Immutable => Type::Tag(TypeTag::Immutable),
ast::Mutability::Mutable => Type::Tag(TypeTag::Mutable),
});

Type::Ref { sharedness, mutability, lifetime }
},
}
}
Expand Down
1 change: 1 addition & 0 deletions src/parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ pub enum Sharedness {

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Mutability {
#[allow(unused)]
Polymorphic,
Immutable,
Mutable,
Expand Down
Loading

0 comments on commit d902bd7

Please sign in to comment.