Skip to content

Commit

Permalink
fix: Disallow yield in defer + fix inference for generators (#106)
Browse files Browse the repository at this point in the history
* fix(lang): Disallow yield in a defered statement

* fix inference
  • Loading branch information
tibordp authored Sep 9, 2024
1 parent 31843fb commit b616323
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 9 deletions.
1 change: 0 additions & 1 deletion src/alumina-boot/src/ast/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1553,7 +1553,6 @@ impl<'ast, 'src> LambdaVisitor<'ast, 'src> {
is_local: true,
is_lambda: true,
is_protocol_fn: false,
is_generator: false,
}));

let bindings = self
Expand Down
8 changes: 6 additions & 2 deletions src/alumina-boot/src/ast/maker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,11 +394,16 @@ impl<'ast> AstItemMaker<'ast> {
body: Option<tree_sitter::Node<'src>>,
attributes: &'ast [Attribute],
) -> Result<(), AluminaError> {
let mut attributes = attributes.to_vec();

let mut parameters: Vec<Parameter<'ast>> = Vec::new();
let code = scope.code().unwrap();

let is_extern = node.child_by_field(FieldKind::Extern).is_some();
let is_generator = node.child_by_field(FieldKind::Generator).is_some();
if is_generator {
attributes.push(Attribute::Generator);
}

let has_varargs = node
.child_by_field(FieldKind::Parameters)
Expand Down Expand Up @@ -516,7 +521,7 @@ impl<'ast> AstItemMaker<'ast> {

let result = Item::Function(Function {
name,
attributes,
attributes: attributes.alloc_on(self.ast),
placeholders,
args: parameters.alloc_on(self.ast),
return_type,
Expand All @@ -526,7 +531,6 @@ impl<'ast> AstItemMaker<'ast> {
is_local: self.local,
is_lambda: false,
is_protocol_fn,
is_generator,
});

item.assign(result);
Expand Down
3 changes: 2 additions & 1 deletion src/alumina-boot/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,6 @@ pub struct Function<'ast> {
pub span: Option<Span>,
pub is_local: bool,
pub is_lambda: bool,
pub is_generator: bool,
pub varargs: bool,
pub is_protocol_fn: bool,
}
Expand Down Expand Up @@ -836,6 +835,7 @@ pub enum Attribute<'ast> {
Intrinsic,
StaticConstructor,
LinkName(&'ast str),
Generator,
}

impl<'ast> Attribute<'ast> {
Expand Down Expand Up @@ -865,6 +865,7 @@ impl<'ast> Attribute<'ast> {
Attribute::ConstOnly => Attribute::ConstOnly,
Attribute::NoConst => Attribute::NoConst,
Attribute::TupleCall => Attribute::TupleCall,
Attribute::Generator => Attribute::Generator,
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/alumina-boot/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ pub enum CodeDiagnostic {
ConstMessage(String),
#[error("cannot defer inside a defered expression")]
DeferInDefer,
#[error("cannot yield inside a defered expression")]
YieldInDefer,
#[error("`...` expressions can only be used in macros")]
EtCeteraOutsideOfMacro,
#[error("`$` identifiers can only be used in macros")]
Expand Down
28 changes: 24 additions & 4 deletions src/alumina-boot/src/ir/mono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1378,7 +1378,8 @@ impl<'a, 'ast, 'ir> Monomorphizer<'a, 'ast, 'ir> {

let return_type = child.lower_type_for_value(func.return_type)?;
let generator_type = func
.is_generator
.attributes
.contains(&ast::Attribute::Generator)
.then(|| child.monomorphize_lang_item(LangItemKind::Generator, [return_type]))
.transpose()?
.map(|i| child.types.named(i));
Expand Down Expand Up @@ -1434,7 +1435,7 @@ impl<'a, 'ast, 'ir> Monomorphizer<'a, 'ast, 'ir> {
return Ok(());
}

if func.is_generator {
if generator_type.is_some() {
let mut grandchild = Self::with_replacements(
child.mono_ctx,
child.replacements.clone(),
Expand Down Expand Up @@ -1610,7 +1611,6 @@ impl<'a, 'ast, 'ir> Monomorphizer<'a, 'ast, 'ir> {
is_local: fun.is_local,
is_lambda: false,
is_protocol_fn: false,
is_generator: fun.is_generator,
}));

result.push(ast::AssociatedFn {
Expand Down Expand Up @@ -2871,7 +2871,23 @@ impl<'a, 'ast, 'ir> Monomorphizer<'a, 'ast, 'ir> {
}

if let Some(return_type_hint) = return_type_hint {
infer_pairs.push((fun.return_type, return_type_hint));
if fun.attributes.contains(&ast::Attribute::Generator) {
let item = self
.mono_ctx
.ast
.lang_item(LangItemKind::Generator)
.with_backtrace(&self.diag)?;

let wrapped = ast::Ty::Generic(
ast::Ty::Item(item).alloc_on(self.mono_ctx.ast),
[fun.return_type].alloc_on(self.mono_ctx.ast),
)
.alloc_on(self.mono_ctx.ast);

infer_pairs.push((wrapped, return_type_hint));
} else {
infer_pairs.push((fun.return_type, return_type_hint));
}
}

let mut type_inferer = TypeInferer::new(
Expand Down Expand Up @@ -5353,6 +5369,10 @@ impl<'a, 'ast, 'ir> Monomorphizer<'a, 'ast, 'ir> {
bail!(self, CodeDiagnostic::YieldOutsideOfGenerator);
}

if self.defer_context.as_ref().is_some_and(|d| d.in_defer) {
bail!(self, CodeDiagnostic::YieldInDefer);
}

let inner = inner
.map(|inner| self.lower_expr(inner, self.yield_type))
.transpose()?
Expand Down
2 changes: 1 addition & 1 deletion sysroot/std/panicking.alu
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ mod internal {

// minicoro does not play well with unwinding, so if we are inside a coroutine,
// we suppress the backtrace to avoid segfaults
#[cfg(coroutines)]{}
#[cfg(coroutines)]
let _ = print_panic_message(&panic_info, !runtime::in_coroutine());
#[cfg(not(coroutines))]
let _ = print_panic_message(&panic_info, true);
Expand Down
10 changes: 10 additions & 0 deletions tests/diag/yield_in_defer.alu
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//! exit_code: 1
fn* gen() {
defer {
yield // diag: error(yield_in_defer): "cannot yield inside a defered expression"
}
}

fn main() {
gen();
}
10 changes: 10 additions & 0 deletions tests/lang/lang.alu
Original file line number Diff line number Diff line change
Expand Up @@ -896,3 +896,13 @@ fn test_tuple_concat() {
assert_eq!((1, 2).concat(()), (1, 2));
assert_eq!(().concat(()), ());
}


#[test]
fn test_generator_inference() {
fn* empty<T>(_: Option<T>) -> T {
}

let _gen: std::runtime::Generator<i32> = empty(Option::none());
let _gen = empty(Option::some(12));
}

0 comments on commit b616323

Please sign in to comment.