Skip to content

Commit

Permalink
WIP: support inline types
Browse files Browse the repository at this point in the history
  • Loading branch information
yorickpeterse committed Nov 7, 2024
1 parent 40aa352 commit 7b068b3
Show file tree
Hide file tree
Showing 18 changed files with 325 additions and 86 deletions.
1 change: 1 addition & 0 deletions ast/src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ pub enum ClassKind {
Enum,
Regular,
Extern,
Inline,
}

#[derive(Debug, PartialEq, Eq)]
Expand Down
26 changes: 26 additions & 0 deletions ast/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,10 @@ impl Parser {
self.next();
ClassKind::Extern
}
TokenKind::Inline => {
self.next();
ClassKind::Inline
}
_ => ClassKind::Regular,
};

Expand Down Expand Up @@ -4661,6 +4665,28 @@ mod tests {
);
}

#[test]
fn test_inline_class() {
assert_eq!(
top(parse("class inline A {}")),
TopLevelExpression::DefineClass(Box::new(DefineClass {
public: false,
name: Constant {
source: None,
name: "A".to_string(),
location: cols(14, 14)
},
kind: ClassKind::Inline,
type_parameters: None,
body: ClassExpressions {
values: Vec::new(),
location: cols(16, 17)
},
location: cols(1, 17)
}))
);
}

#[test]
fn test_async_class() {
assert_eq!(
Expand Down
1 change: 1 addition & 0 deletions compiler/src/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ impl<'a> GenerateDocumentation<'a> {
ClassKind::Enum => " enum",
ClassKind::Async => " async",
ClassKind::Extern => " extern",
ClassKind::Inline => "inline",
_ if id.is_builtin() => " builtin",
_ => "",
},
Expand Down
1 change: 1 addition & 0 deletions compiler/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,7 @@ impl Document {
nodes::ClassKind::Builtin => header.push(Node::text("builtin ")),
nodes::ClassKind::Enum => header.push(Node::text("enum ")),
nodes::ClassKind::Extern => header.push(Node::text("extern ")),
nodes::ClassKind::Inline => header.push(Node::text("inline ")),
nodes::ClassKind::Regular => {}
}

Expand Down
43 changes: 43 additions & 0 deletions compiler/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ pub(crate) enum ClassKind {
Builtin,
Enum,
Regular,
Inline,
}

#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -1327,6 +1328,7 @@ impl<'a> LowerToHir<'a> {
ast::ClassKind::Async => ClassKind::Async,
ast::ClassKind::Enum => ClassKind::Enum,
ast::ClassKind::Builtin => ClassKind::Builtin,
ast::ClassKind::Inline => ClassKind::Inline,
_ => ClassKind::Regular,
},
name: self.constant(node.name),
Expand Down Expand Up @@ -4018,6 +4020,47 @@ mod tests {
);
}

#[test]
fn test_lower_inline_class() {
let hir = lower_top_expr("class inline A { let @a: B }").0;

assert_eq!(
hir,
TopLevelExpression::Class(Box::new(DefineClass {
documentation: String::new(),
public: false,
class_id: None,
kind: ClassKind::Inline,
name: Constant {
name: "A".to_string(),
location: cols(14, 14)
},
body: vec![ClassExpression::Field(Box::new(DefineField {
documentation: String::new(),
public: false,
field_id: None,
name: Identifier {
name: "a".to_string(),
location: cols(22, 23)
},
value_type: Type::Named(Box::new(TypeName {
source: None,
resolved_type: types::TypeRef::Unknown,
name: Constant {
name: "B".to_string(),
location: cols(26, 26)
},
arguments: Vec::new(),
location: cols(26, 26)
})),
location: cols(18, 26),
}))],
type_parameters: Vec::new(),
location: cols(1, 28)
})),
);
}

#[test]
fn test_lower_public_class() {
let hir = lower_top_expr("class pub A {}").0;
Expand Down
18 changes: 7 additions & 11 deletions compiler/src/llvm/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,18 +185,14 @@ impl Context {
TypeId::ClassInstance(ins) => {
let cls = ins.instance_of();

if cls.kind(db).is_extern() {
layouts.instances[ins.instance_of().0 as usize]
.as_basic_type_enum()
} else {
match cls.0 {
BOOL_ID | NIL_ID => {
self.bool_type().as_basic_type_enum()
}
INT_ID => self.i64_type().as_basic_type_enum(),
FLOAT_ID => self.f64_type().as_basic_type_enum(),
_ => self.pointer_type().as_basic_type_enum(),
match cls.0 {
BOOL_ID | NIL_ID => self.bool_type().as_basic_type_enum(),
INT_ID => self.i64_type().as_basic_type_enum(),
FLOAT_ID => self.f64_type().as_basic_type_enum(),
_ if cls.kind(db).is_stack_allocated() => {
layouts.instances[cls.0 as usize].as_basic_type_enum()
}
_ => self.pointer_type().as_basic_type_enum(),
}
}
_ => self.pointer_type().as_basic_type_enum(),
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/llvm/layouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ impl<'ctx> Layouts<'ctx> {
// processed here will have.
let mut types = Vec::with_capacity(fields.len() + 1);

if kind.is_extern() {
if kind.is_stack_allocated() {
for field in fields {
let typ =
context.llvm_type(db, &layouts, field.value_type(db));
Expand Down
20 changes: 16 additions & 4 deletions compiler/src/llvm/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2117,7 +2117,10 @@ impl<'shared, 'module, 'ctx> LowerMethod<'shared, 'module, 'ctx> {
);
}
Instruction::GetField(ins)
if ins.class.kind(&self.shared.state.db).is_extern() =>
if ins
.class
.kind(&self.shared.state.db)
.is_stack_allocated() =>
{
let reg_var = self.variables[&ins.register];
let rec_var = self.variables[&ins.receiver];
Expand All @@ -2141,7 +2144,10 @@ impl<'shared, 'module, 'ctx> LowerMethod<'shared, 'module, 'ctx> {
self.builder.store(reg_var, field);
}
Instruction::SetField(ins)
if ins.class.kind(&self.shared.state.db).is_extern() =>
if ins
.class
.kind(&self.shared.state.db)
.is_stack_allocated() =>
{
let rec_var = self.variables[&ins.receiver];
let rec_typ = self.variable_types[&ins.receiver];
Expand Down Expand Up @@ -2218,7 +2224,10 @@ impl<'shared, 'module, 'ctx> LowerMethod<'shared, 'module, 'ctx> {
self.builder.store(reg_var, field);
}
Instruction::FieldPointer(ins)
if ins.class.kind(&self.shared.state.db).is_extern() =>
if ins
.class
.kind(&self.shared.state.db)
.is_stack_allocated() =>
{
let reg_var = self.variables[&ins.register];
let rec_var = self.variables[&ins.receiver];
Expand Down Expand Up @@ -2360,7 +2369,10 @@ impl<'shared, 'module, 'ctx> LowerMethod<'shared, 'module, 'ctx> {
self.builder.branch(is_zero, drop_block, after_block);
}
Instruction::Allocate(ins)
if ins.class.kind(&self.shared.state.db).is_extern() =>
if ins
.class
.kind(&self.shared.state.db)
.is_stack_allocated() =>
{
// Defining the alloca already reserves (uninitialised) memory,
// so there's nothing we actually need to do here. Setting the
Expand Down
6 changes: 5 additions & 1 deletion compiler/src/mir/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ fn instruction_weight(db: &Database, instruction: &Instruction) -> u16 {
// give them a weight of zero. Regular allocations and spawning
// processes translate into a function call, so we give them the same
// weight as calls.
Instruction::Allocate(ins) if ins.class.kind(db).is_extern() => 0,
Instruction::Allocate(ins)
if ins.class.kind(db).is_stack_allocated() =>
{
0
}
Instruction::Allocate(_) => 1,
Instruction::Spawn(_) => 1,

Expand Down
16 changes: 9 additions & 7 deletions compiler/src/mir/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -601,13 +601,15 @@ impl<'a> GenerateDropper<'a> {
lower.drop_register(reg, loc);
}

// Destructors may introduce new references, so we have to check again.
// We do this _after_ processing fields so we can correctly drop cyclic
// types.
lower.current_block_mut().check_refs(self_reg, loc);

if free_self {
lower.current_block_mut().free(self_reg, class, loc);
if !class.is_stack_allocated(lower.db()) {
// Destructors may introduce new references, so we have to check
// again. We do this _after_ processing fields so we can correctly
// drop cyclic types.
lower.current_block_mut().check_refs(self_reg, loc);

if free_self {
lower.current_block_mut().free(self_reg, class, loc);
}
}

if terminate {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/mir/pattern_matching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -830,7 +830,7 @@ impl<'a> Compiler<'a> {

Type::Finite(cons)
}
ClassKind::Regular | ClassKind::Extern => {
ClassKind::Regular | ClassKind::Extern | ClassKind::Inline => {
let fields = class_id.fields(self.db());
let args = fields
.iter()
Expand Down
14 changes: 8 additions & 6 deletions compiler/src/mir/specialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1295,8 +1295,10 @@ impl<'a, 'b, 'c> ExpandDrop<'a, 'b, 'c> {
.class_id(self.db)
.unwrap();

self.block_mut(before_id).check_refs(value, location);
self.block_mut(before_id).free(value, class, location);
if !class.is_stack_allocated(self.db) {
self.block_mut(before_id).check_refs(value, location);
self.block_mut(before_id).free(value, class, location);
}
}

self.block_mut(before_id).goto(after_id, location);
Expand Down Expand Up @@ -1397,13 +1399,13 @@ impl<'a, 'b, 'c> ExpandReference<'a, 'b, 'c> {
let reg = ins.register;
let val = ins.value;
let typ = self.method.registers.value_type(val);
let is_extern = typ
let is_stack = typ
.class_id(self.db)
.map_or(false, |i| i.kind(self.db).is_extern());
.map_or(false, |i| i.kind(self.db).is_stack_allocated());

match typ.shape(self.db, self.shapes) {
Shape::Owned if is_extern || typ.is_permanent(self.db) => {
// Extern and permanent values are to be left as-is.
Shape::Owned if is_stack || typ.is_permanent(self.db) => {
// stack and permanent values are to be left as-is.
}
Shape::Int(_, _)
| Shape::Float(_)
Expand Down
76 changes: 30 additions & 46 deletions compiler/src/type_check/define_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,55 +68,39 @@ impl<'a> DefineTypes<'a> {
let module = self.module;
let vis = Visibility::public(node.public);
let loc = node.location;
let id = match node.kind {
hir::ClassKind::Builtin => {
if !self.module.is_std(self.db()) {
self.state.diagnostics.error(
DiagnosticId::InvalidType,
"builtin classes can only be defined in 'std' modules",
self.file(),
node.location,
);
}
let id = if let hir::ClassKind::Builtin = node.kind {
if !self.module.is_std(self.db()) {
self.state.diagnostics.error(
DiagnosticId::InvalidType,
"builtin classes can only be defined in 'std' modules",
self.file(),
node.location,
);
}

if let Some(id) = self.db().builtin_class(&name) {
id.set_module(self.db_mut(), module);
id
} else {
self.state.diagnostics.error(
DiagnosticId::InvalidType,
format!("'{}' isn't a valid builtin class", name),
self.file(),
node.location,
);
if let Some(id) = self.db().builtin_class(&name) {
id.set_module(self.db_mut(), module);
id
} else {
self.state.diagnostics.error(
DiagnosticId::InvalidType,
format!("'{}' isn't a valid builtin class", name),
self.file(),
node.location,
);

return;
}
return;
}
hir::ClassKind::Regular => Class::alloc(
self.db_mut(),
name.clone(),
ClassKind::Regular,
vis,
module,
loc,
),
hir::ClassKind::Async => Class::alloc(
self.db_mut(),
name.clone(),
ClassKind::Async,
vis,
module,
loc,
),
hir::ClassKind::Enum => Class::alloc(
self.db_mut(),
name.clone(),
ClassKind::Enum,
vis,
module,
loc,
),
} else {
let kind = match node.kind {
hir::ClassKind::Regular => ClassKind::Regular,
hir::ClassKind::Async => ClassKind::Async,
hir::ClassKind::Enum => ClassKind::Enum,
hir::ClassKind::Inline => ClassKind::Inline,
_ => unreachable!(),
};

Class::alloc(self.db_mut(), name.clone(), kind, vis, module, loc)
};

if self.module.symbol_exists(self.db(), &name) {
Expand Down
7 changes: 6 additions & 1 deletion compiler/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub(crate) mod expressions;
pub(crate) mod imports;
pub(crate) mod methods;

#[derive(Eq, PartialEq)]
#[derive(Eq, PartialEq, Debug)]
enum RefKind {
Default,
Owned,
Expand Down Expand Up @@ -270,6 +270,11 @@ impl<'a> DefineTypeSignature<'a> {
id,
)))
}
Symbol::Class(id)
if id.kind(&self.state.db).is_stack_allocated() =>
{
TypeRef::Owned(self.define_class_instance(id, node))
}
Symbol::Class(id) => {
kind.into_type_ref(self.define_class_instance(id, node))
}
Expand Down
Loading

0 comments on commit 7b068b3

Please sign in to comment.