From 3476d061a803087179c6b1874289fc71a3ac3e99 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 27 Nov 2024 18:03:06 +0100 Subject: [PATCH] Add support for T: inline --- ast/src/nodes.rs | 2 + ast/src/parser.rs | 32 ++++++ compiler/src/diagnostics.rs | 25 ++++- compiler/src/format.rs | 15 ++- compiler/src/hir.rs | 102 ++++++++++++------ compiler/src/type_check/define_types.rs | 6 +- compiler/src/type_check/methods.rs | 4 + compiler/src/type_check/mod.rs | 4 + .../diagnostics/inline_type_parameters.inko | 15 +++ .../diagnostics/type_parameter_bounds.inko | 12 +++ .../type_parameter_requirements.inko | 10 ++ std/fixtures/fmt/type_parameters/input.inko | 11 ++ std/fixtures/fmt/type_parameters/output.inko | 11 ++ 13 files changed, 208 insertions(+), 41 deletions(-) create mode 100644 std/fixtures/diagnostics/inline_type_parameters.inko create mode 100644 std/fixtures/diagnostics/type_parameter_bounds.inko create mode 100644 std/fixtures/diagnostics/type_parameter_requirements.inko create mode 100644 std/fixtures/fmt/type_parameters/input.inko create mode 100644 std/fixtures/fmt/type_parameters/output.inko diff --git a/ast/src/nodes.rs b/ast/src/nodes.rs index c31c0630..2e0eefe9 100644 --- a/ast/src/nodes.rs +++ b/ast/src/nodes.rs @@ -638,6 +638,7 @@ impl Node for ReopenClass { pub enum Requirement { Trait(TypeName), Mutable(Location), + Inline(Location), } impl Node for Requirement { @@ -645,6 +646,7 @@ impl Node for Requirement { match self { Requirement::Trait(n) => &n.location, Requirement::Mutable(loc) => loc, + Requirement::Inline(loc) => loc, } } } diff --git a/ast/src/parser.rs b/ast/src/parser.rs index e36dc760..a65c5218 100644 --- a/ast/src/parser.rs +++ b/ast/src/parser.rs @@ -1291,6 +1291,7 @@ impl Parser { let token = self.require()?; let req = match token.kind { TokenKind::Mut => Requirement::Mutable(token.location), + TokenKind::Inline => Requirement::Inline(token.location), _ => Requirement::Trait( self.type_name_with_optional_namespace(token)?, ), @@ -5553,6 +5554,37 @@ mod tests { })) ); + assert_eq!( + top(parse("impl A if T: inline {}")), + TopLevelExpression::ReopenClass(Box::new(ReopenClass { + class_name: Constant { + source: None, + name: "A".to_string(), + location: cols(6, 6) + }, + body: ImplementationExpressions { + values: Vec::new(), + location: cols(21, 22) + }, + bounds: Some(TypeBounds { + values: vec![TypeBound { + name: Constant { + source: None, + name: "T".to_string(), + location: cols(11, 11) + }, + requirements: Requirements { + values: vec![Requirement::Inline(cols(14, 19))], + location: cols(14, 19) + }, + location: cols(11, 19) + }], + location: cols(11, 19) + }), + location: cols(1, 19) + })) + ); + assert_eq!( top(parse("impl A if T: mut, {}")), TopLevelExpression::ReopenClass(Box::new(ReopenClass { diff --git a/compiler/src/diagnostics.rs b/compiler/src/diagnostics.rs index bb1596c6..1b798f7c 100644 --- a/compiler/src/diagnostics.rs +++ b/compiler/src/diagnostics.rs @@ -859,15 +859,34 @@ impl Diagnostics { ); } - pub(crate) fn type_parameter_already_mutable( + pub(crate) fn duplicate_type_parameter_requirement( + &mut self, + param: &str, + req: &str, + file: PathBuf, + location: Location, + ) { + self.error( + DiagnosticId::InvalidType, + format!( + "type parameter '{}' already defines the '{}' requirement", + param, req + ), + file, + location, + ); + } + + pub(crate) fn mutable_inline_type_parameter( &mut self, - name: &str, file: PathBuf, location: Location, ) { self.error( DiagnosticId::InvalidType, - format!("the type parameter '{}' is already mutable", name), + "type parameters can't be both 'mut' and 'inline', \ + as 'inline' types are immutable" + .to_string(), file, location, ); diff --git a/compiler/src/format.rs b/compiler/src/format.rs index c82d358e..c2fce5b5 100644 --- a/compiler/src/format.rs +++ b/compiler/src/format.rs @@ -2352,18 +2352,22 @@ impl Document { &mut self, nodes: &nodes::Requirements, ) -> Node { + use Ordering::*; + let mut pairs = Vec::new(); let mut reqs = nodes.values.iter().collect::>(); reqs.sort_by(|a, b| match (a, b) { - (Requirement::Mutable(_), Requirement::Mutable(_)) => { - Ordering::Equal - } - (Requirement::Mutable(_), _) => Ordering::Less, - (_, Requirement::Mutable(_)) => Ordering::Greater, + (Requirement::Inline(_), Requirement::Inline(_)) => Equal, + (Requirement::Mutable(_), Requirement::Mutable(_)) => Equal, (Requirement::Trait(lhs), Requirement::Trait(rhs)) => { lhs.name.name.cmp(&rhs.name.name) } + (Requirement::Inline(_), _) => Less, + (Requirement::Mutable(_), Requirement::Inline(_)) => Greater, + (Requirement::Mutable(_), Requirement::Trait(_)) => Less, + (Requirement::Trait(_), Requirement::Mutable(_)) => Greater, + (Requirement::Trait(_), Requirement::Inline(_)) => Greater, }); for (idx, node) in reqs.into_iter().enumerate() { @@ -2377,6 +2381,7 @@ impl Document { let val = match node { nodes::Requirement::Trait(n) => self.type_name(n, None), nodes::Requirement::Mutable(_) => Node::text("mut"), + nodes::Requirement::Inline(_) => Node::text("inline"), }; pair.push(val); diff --git a/compiler/src/hir.rs b/compiler/src/hir.rs index 305bb81b..29714848 100644 --- a/compiler/src/hir.rs +++ b/compiler/src/hir.rs @@ -472,6 +472,7 @@ pub(crate) struct TypeBound { pub(crate) name: Constant, pub(crate) requirements: Vec, pub(crate) mutable: bool, + pub(crate) inline: bool, pub(crate) location: Location, } @@ -644,6 +645,7 @@ pub(crate) struct TypeParameter { pub(crate) name: Constant, pub(crate) requirements: Vec, pub(crate) mutable: bool, + pub(crate) inline: bool, pub(crate) location: Location, } @@ -1572,28 +1574,68 @@ impl<'a> LowerToHir<'a> { fn type_bound(&mut self, node: ast::TypeBound) -> TypeBound { let name = self.constant(node.name); + let (reqs, mutable, inline) = self.define_type_parameter_requirements( + &name.name, + node.requirements.values, + ); + + TypeBound { + name, + requirements: reqs, + mutable, + inline, + location: node.location, + } + } + + fn define_type_parameter_requirements( + &mut self, + name: &str, + nodes: Vec, + ) -> (Vec, bool, bool) { let mut mutable = false; + let mut inline = false; let mut requirements = Vec::new(); - for req in node.requirements.values { + for req in nodes { match req { ast::Requirement::Trait(n) => { requirements.push(self.type_name(n)) } ast::Requirement::Mutable(loc) if mutable => { - self.state.diagnostics.type_parameter_already_mutable( - &name.name, - self.file(), - loc, - ); + let file = self.file(); + + self.state + .diagnostics + .duplicate_type_parameter_requirement( + name, "mut", file, loc, + ); } - ast::Requirement::Mutable(_) => { - mutable = true; + ast::Requirement::Mutable(loc) if inline => { + self.state + .diagnostics + .mutable_inline_type_parameter(self.file(), loc); } + ast::Requirement::Inline(loc) if inline => { + let file = self.file(); + + self.state + .diagnostics + .duplicate_type_parameter_requirement( + name, "mut", file, loc, + ); + } + ast::Requirement::Inline(loc) if mutable => { + self.state + .diagnostics + .mutable_inline_type_parameter(self.file(), loc); + } + ast::Requirement::Mutable(_) => mutable = true, + ast::Requirement::Inline(_) => inline = true, } } - TypeBound { name, requirements, mutable, location: node.location } + (requirements, mutable, inline) } fn define_trait( @@ -1903,35 +1945,19 @@ impl<'a> LowerToHir<'a> { fn type_parameter(&mut self, node: ast::TypeParameter) -> TypeParameter { let name = self.constant(node.name); let location = node.location; - let mut mutable = false; - let mut requirements = Vec::new(); - - if let Some(reqs) = node.requirements { - for req in reqs.values { - match req { - ast::Requirement::Trait(n) => { - requirements.push(self.type_name(n)) - } - ast::Requirement::Mutable(loc) if mutable => { - self.state.diagnostics.type_parameter_already_mutable( - &name.name, - self.file(), - loc, - ); - } - ast::Requirement::Mutable(_) => { - mutable = true; - } - } - } - } + let (reqs, mutable, inline) = if let Some(reqs) = node.requirements { + self.define_type_parameter_requirements(&name.name, reqs.values) + } else { + (Vec::new(), false, false) + }; TypeParameter { type_parameter_id: None, name, - requirements, + requirements: reqs, location, mutable, + inline, } } @@ -3760,6 +3786,7 @@ mod tests { location: cols(11, 11) }], mutable: false, + inline: false, location: cols(8, 11) }], arguments: vec![MethodArgument { @@ -3961,6 +3988,7 @@ mod tests { location: cols(12, 12) }], mutable: false, + inline: false, location: cols(9, 12) }], body: vec![ClassExpression::Field(Box::new(DefineField { @@ -4164,6 +4192,7 @@ mod tests { location: cols(20, 20) }], mutable: false, + inline: false, location: cols(17, 20) }], body: vec![ClassExpression::Field(Box::new(DefineField { @@ -4246,6 +4275,7 @@ mod tests { }, requirements: Vec::new(), mutable: false, + inline: false, location: cols(23, 23) }], arguments: vec![MethodArgument { @@ -4321,6 +4351,7 @@ mod tests { }, requirements: Vec::new(), mutable: false, + inline: false, location: cols(22, 22) }], arguments: vec![MethodArgument { @@ -4396,6 +4427,7 @@ mod tests { }, requirements: Vec::new(), mutable: false, + inline: false, location: cols(16, 16) }], arguments: vec![MethodArgument { @@ -4509,6 +4541,7 @@ mod tests { }, requirements: Vec::new(), mutable: false, + inline: false, location: cols(9, 9) }], requirements: vec![TypeName { @@ -4579,6 +4612,7 @@ mod tests { }, requirements: Vec::new(), mutable: false, + inline: false, location: cols(16, 16) }], arguments: vec![MethodArgument { @@ -4718,6 +4752,7 @@ mod tests { }, requirements: Vec::new(), mutable: false, + inline: false, location: cols(16, 16) }], arguments: vec![MethodArgument { @@ -4828,6 +4863,7 @@ mod tests { }, requirements: Vec::new(), mutable: true, + inline: false, location: cols(11, 16), }], body: Vec::new(), @@ -5083,6 +5119,7 @@ mod tests { location: cols(20, 20) },], mutable: true, + inline: false, location: cols(17, 26) }], body: Vec::new(), @@ -6754,6 +6791,7 @@ mod tests { }, requirements: Vec::new(), mutable: false, + inline: false, location: cols(19, 19) }], body: vec![ diff --git a/compiler/src/type_check/define_types.rs b/compiler/src/type_check/define_types.rs index 8be9a4fb..347ef441 100644 --- a/compiler/src/type_check/define_types.rs +++ b/compiler/src/type_check/define_types.rs @@ -781,7 +781,7 @@ impl<'a> DefineTypeParameters<'a> { pid.set_mutable(self.db_mut()); } - if is_stack { + if is_stack || param.inline { pid.set_stack_allocated(self.db_mut()); } @@ -809,6 +809,10 @@ impl<'a> DefineTypeParameters<'a> { pid.set_mutable(self.db_mut()); } + if param.inline { + pid.set_stack_allocated(self.db_mut()); + } + param.type_parameter_id = Some(pid); } } diff --git a/compiler/src/type_check/methods.rs b/compiler/src/type_check/methods.rs index aae45075..483616a9 100644 --- a/compiler/src/type_check/methods.rs +++ b/compiler/src/type_check/methods.rs @@ -97,6 +97,10 @@ trait MethodDefiner { pid.set_mutable(self.db_mut()); } + if param_node.inline { + pid.set_stack_allocated(self.db_mut()); + } + param_node.type_parameter_id = Some(pid); } } diff --git a/compiler/src/type_check/mod.rs b/compiler/src/type_check/mod.rs index 686436be..8a6390a8 100644 --- a/compiler/src/type_check/mod.rs +++ b/compiler/src/type_check/mod.rs @@ -931,6 +931,10 @@ pub(crate) fn define_type_bounds( new_param.set_mutable(&mut state.db); } + if bound.inline { + new_param.set_stack_allocated(&mut state.db); + } + new_param.add_requirements(&mut state.db, reqs); bounds.set(param, new_param); } diff --git a/std/fixtures/diagnostics/inline_type_parameters.inko b/std/fixtures/diagnostics/inline_type_parameters.inko new file mode 100644 index 00000000..17b5f109 --- /dev/null +++ b/std/fixtures/diagnostics/inline_type_parameters.inko @@ -0,0 +1,15 @@ +class Example[T: inline] { + let @value: T +} + +fn example[T: inline](value: T) {} + +fn examples { + Example(42) + Example([10]) + example(42) + example([10]) +} + +# inline_type_parameters.inko:9:11 error(invalid-type): expected a value of type 'T: inline', found 'Array[Int]' +# inline_type_parameters.inko:11:11 error(invalid-type): expected a value of type 'T: inline', found 'Array[Int]' diff --git a/std/fixtures/diagnostics/type_parameter_bounds.inko b/std/fixtures/diagnostics/type_parameter_bounds.inko new file mode 100644 index 00000000..2af0f053 --- /dev/null +++ b/std/fixtures/diagnostics/type_parameter_bounds.inko @@ -0,0 +1,12 @@ +class A[T] {} + +impl A if T: mut {} + +impl A if T: inline {} + +impl A if T: mut + inline {} + +impl A if T: inline + mut {} + +# type_parameter_bounds.inko:7:20 error(invalid-type): type parameters can't be both 'mut' and 'inline', as 'inline' types are immutable +# type_parameter_bounds.inko:9:23 error(invalid-type): type parameters can't be both 'mut' and 'inline', as 'inline' types are immutable diff --git a/std/fixtures/diagnostics/type_parameter_requirements.inko b/std/fixtures/diagnostics/type_parameter_requirements.inko new file mode 100644 index 00000000..1139fe08 --- /dev/null +++ b/std/fixtures/diagnostics/type_parameter_requirements.inko @@ -0,0 +1,10 @@ +class A[T: mut] {} + +class B[T: inline] {} + +class C[T: mut + inline] {} + +class D[T: inline + mut] {} + +# type_parameter_requirements.inko:5:18 error(invalid-type): type parameters can't be both 'mut' and 'inline', as 'inline' types are immutable +# type_parameter_requirements.inko:7:21 error(invalid-type): type parameters can't be both 'mut' and 'inline', as 'inline' types are immutable diff --git a/std/fixtures/fmt/type_parameters/input.inko b/std/fixtures/fmt/type_parameters/input.inko new file mode 100644 index 00000000..9294c262 --- /dev/null +++ b/std/fixtures/fmt/type_parameters/input.inko @@ -0,0 +1,11 @@ +fn a[P: A] {} + +fn b[P: B + A] {} + +fn c[P: B + A + mut] {} + +fn d[P: B + A + inline] {} + +fn e[P: B + A + inline + mut] {} + +fn f[P: mut + inline] {} diff --git a/std/fixtures/fmt/type_parameters/output.inko b/std/fixtures/fmt/type_parameters/output.inko new file mode 100644 index 00000000..084aa169 --- /dev/null +++ b/std/fixtures/fmt/type_parameters/output.inko @@ -0,0 +1,11 @@ +fn a[P: A] {} + +fn b[P: A + B] {} + +fn c[P: mut + A + B] {} + +fn d[P: inline + A + B] {} + +fn e[P: inline + mut + A + B] {} + +fn f[P: inline + mut] {}