parsing struct definitions in ast

This commit is contained in:
Janis 2024-09-14 13:09:17 +02:00
parent de22a9e01a
commit a565c1e1e9
3 changed files with 209 additions and 240 deletions

View file

@ -620,8 +620,7 @@ pub mod intern {
fields, fields,
} => { } => {
let flags = StructFlags::new(packed, c_like, fields.len() as u32).pack(); let flags = StructFlags::new(packed, c_like, fields.len() as u32).pack();
let i = self.push_word(name.into_u32()); let i = self.extend_words([name.into_u32(), flags]);
let i = self.push_word(flags);
self.extend_words( self.extend_words(
fields fields
.into_iter() .into_iter()
@ -1031,6 +1030,10 @@ enum ParseError {
#[error("Dummy Message.")] #[error("Dummy Message.")]
UnmatchedParens(u32), UnmatchedParens(u32),
#[error("Dummy Message.")] #[error("Dummy Message.")]
ExpectedTypeDeclaration,
#[error("Dummy Message.")]
UnexpectedTypeAttributes,
#[error("Dummy Message.")]
UnmatchedSquareBracket(u32), UnmatchedSquareBracket(u32),
#[error("Dummy Message.")] #[error("Dummy Message.")]
ExpectedEndOfBlock, ExpectedEndOfBlock,
@ -1697,10 +1700,6 @@ impl Ast {
(i, self.extra.len() as u32) (i, self.extra.len() as u32)
} }
fn set_tag_data_source_loc(&mut self, index: Index, tag: Tag, data: Data, loc: SourceLocation) { fn set_tag_data_source_loc(&mut self, index: Index, tag: Tag, data: Data, loc: SourceLocation) {
eprintln!(
"{index} <- ({tag:?}, {:?}, {loc})",
ExpandedData::from((tag, data))
);
self.tags[index.index()] = tag; self.tags[index.index()] = tag;
self.datas[index.index()] = data; self.datas[index.index()] = data;
self.source_locs[index.index()] = loc; self.source_locs[index.index()] = loc;
@ -1995,13 +1994,11 @@ pub mod ast_gen {
loc: tokens.current_source_location(), loc: tokens.current_source_location(),
})?; })?;
eprintln!("pointer qualifiers: {:?}", tokens.peek_token());
let &[cnst, vol, noalias] = let &[cnst, vol, noalias] =
&tokens.eat_all_zero_or_once(&[Token::Const, Token::Volatile, Token::Noalias])[..3] &tokens.eat_all_zero_or_once(&[Token::Const, Token::Volatile, Token::Noalias])[..3]
else { else {
unreachable!() unreachable!()
}; };
eprintln!("pointee: {:?}", tokens.peek_token());
let pointee = self.parse_type(tokens)?; let pointee = self.parse_type(tokens)?;
Ok(self Ok(self
@ -2289,7 +2286,6 @@ pub mod ast_gen {
} }
}; };
eprintln!("semi: {:?}", tokens.peek_token());
let Some(_) = tokens.eat_token(Token::Semi) else { let Some(_) = tokens.eat_token(Token::Semi) else {
break 'blk ErrorInfo { break 'blk ErrorInfo {
error: ParseError::ExpectedToken(Token::Semi), error: ParseError::ExpectedToken(Token::Semi),
@ -2477,6 +2473,9 @@ pub mod ast_gen {
loc: tokens.current_source_location(), loc: tokens.current_source_location(),
})?; })?;
if let Some(decl) = self.parse_constant_decls(tokens)? {
statements.push(decl);
} else {
match next.token() { match next.token() {
Token::CloseBrace => { Token::CloseBrace => {
break None; break None;
@ -2487,59 +2486,30 @@ pub mod ast_gen {
Token::Var | Token::Let => { Token::Var | Token::Let => {
statements.push(self.parse_var_decl(tokens)?); statements.push(self.parse_var_decl(tokens)?);
} }
Token::Const => {
statements.push(self.parse_const_decl(tokens)?);
}
Token::Fn => {
statements.push(self.parse_fn_decl(tokens));
}
_ => { _ => {
if self.is_statement(tokens) { if self.is_statement(tokens) {
// expr -> statements // expr -> statements
let expr = match self.parse_expr(tokens) { let expr = self
Ok(i) => { .parse_with_trailing_semi(tokens, |this, tokens| {
_ = tokens.eat_token(Token::Semi).ok_or(ErrorInfo { this.parse_expr(tokens)
error: ParseError::ExpectedToken(Token::Semi),
loc: tokens.current_source_location(),
})?; })?;
i
}
Err(err) => {
tokens.advance_past_semi().ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Semi),
loc: tokens.current_source_location(),
})?;
self.push_error(err.error, err.loc)
}
};
statements.push(expr); statements.push(expr);
} else { } else {
// expr -> trailing // expr -> trailing
let expr = match self.parse_expr(tokens) { let expr = self.parse_expr(tokens)?;
Ok(i) => {
if !tokens.is_next_token(Token::CloseBrace) { if !tokens.is_next_token(Token::CloseBrace) {
return Err(ErrorInfo { statements.push(self.push_error(
error: ParseError::ExpectedEndOfBlock, ParseError::ExpectedEndOfBlock,
loc: tokens.current_source_location(), tokens.current_source_location(),
}); ));
} } else {
i
}
Err(err) => {
tokens.advance_past_end_of_braced().ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::CloseBrace),
loc: tokens.current_source_location(),
})?;
self.push_error(err.error, err.loc)
}
};
break Some(expr); break Some(expr);
} }
} }
} }
}
}
}; };
self.ast.set_block(block, statements, trailing, loc); self.ast.set_block(block, statements, trailing, loc);
@ -2549,38 +2519,15 @@ pub mod ast_gen {
/// BLOCK <- /// BLOCK <-
/// { STATEMENT* EXPRESSION? } /// { STATEMENT* EXPRESSION? }
fn parse_block(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> { fn parse_block(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
let loc = tokens.current_source_location(); let block = self.parse_braced(tokens, |this, tokens| {
let block = this.ast.reserve_node();
this.push_scope(block, intern::Index::invalid());
let block_result = this.parse_block_inner(block, tokens);
this.pop_scope();
let open_brace = tokens.eat_token(Token::OpenBrace).ok_or(ErrorInfo { block_result
error: ParseError::ExpectedToken(Token::OpenBrace),
loc,
})?; })?;
let block = self.ast.reserve_node();
self.push_scope(block, intern::Index::invalid());
let block_result = self.parse_block_inner(block, tokens);
self.pop_scope();
let block = match block_result {
Ok(i) => {
let Some(_) = tokens.eat_token(Token::CloseBrace) else {
return Err(ErrorInfo {
error: ParseError::UnmatchedBrace(open_brace.token_pos().start),
loc: tokens.current_source_location(),
});
};
i
}
Err(err) => {
tokens.advance_past_end_of_braced().ok_or(ErrorInfo {
error: ParseError::UnmatchedBrace(open_brace.token_pos().start),
loc: tokens.current_source_location(),
})?;
self.push_error(err.error, err.loc)
}
};
Ok(block) Ok(block)
} }
@ -2611,16 +2558,13 @@ pub mod ast_gen {
/// IDENT : TYPENAME /// IDENT : TYPENAME
fn parse_parameter(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> { fn parse_parameter(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
let loc = tokens.current_source_location(); let loc = tokens.current_source_location();
eprintln!("param name: {:?}", tokens.peek_token());
let name = self.parse_ident(tokens)?; let name = self.parse_ident(tokens)?;
eprintln!("colon: {:?}", tokens.peek_token());
let Some(_) = tokens.eat_token(Token::Colon) else { let Some(_) = tokens.eat_token(Token::Colon) else {
return Err(ErrorInfo { return Err(ErrorInfo {
error: ParseError::ExpectedToken(Token::Colon), error: ParseError::ExpectedToken(Token::Colon),
loc, loc,
}); });
}; };
eprintln!("param type: {:?}", tokens.peek_token());
let ty = self.parse_type(tokens)?; let ty = self.parse_type(tokens)?;
let param = self.ast.push_parameter(name, ty, loc); let param = self.ast.push_parameter(name, ty, loc);
@ -2710,27 +2654,8 @@ pub mod ast_gen {
} }
Token::OpenParens => { Token::OpenParens => {
_ = tokens.next(); let expr =
self.parse_parenthesised(tokens, |this, tokens| this.parse_expr(tokens))?;
let expr = match self.parse_expr(tokens) {
Ok(i) => {
let Some(_) = tokens.eat_token(Token::CloseParens) else {
return Err(ErrorInfo {
error: ParseError::UnmatchedParens(next.token_pos().start),
loc,
});
};
i
}
Err(err) => {
tokens.advance_past_end_of_parens().ok_or(ErrorInfo {
error: ParseError::UnmatchedParens(next.token_pos().start),
loc: tokens.current_source_location(),
})?;
self.push_error(err.error, err.loc)
}
};
return Ok(expr); return Ok(expr);
} }
@ -2769,51 +2694,19 @@ pub mod ast_gen {
let loc = next.source_location(); let loc = next.source_location();
match next.token() { match next.token() {
Token::OpenParens => { Token::OpenParens => {
let rhs = if tokens.is_next_token(Token::CloseParens) { let arguments = self.parse_parenthesised(tokens, |this, tokens| {
self.ast.push_argument_list([], loc) if tokens.is_next_token(Token::CloseParens) {
Ok(this.ast.push_argument_list([], loc))
} else { } else {
match self.parse_argument_list(tokens) { this.parse_argument_list(tokens)
Ok(i) => {
_ = tokens.eat_token(Token::Comma);
let Some(_) = tokens.eat_token(Token::CloseParens) else {
let loc = tokens.current_source_location();
return Err(ErrorInfo {
error: ParseError::UnmatchedParens(
next.token_pos().start,
),
loc,
});
};
i
} }
Err(err) => {
tokens.advance_past_end_of_parens().ok_or(ErrorInfo {
error: ParseError::UnmatchedParens(next.token_pos().start),
loc: tokens.current_source_location(),
})?; })?;
self.push_error(err.error, err.loc) return Ok(self.ast.push_call_expr(lhs, arguments, loc));
}
}
};
return Ok(self.ast.push_call_expr(lhs, rhs, loc));
} }
Token::OpenSquareBracket => { Token::OpenSquareBracket => {
let subscript = match self.parse_expr(tokens) { let subscript =
Ok(i) => i, self.parse_bracketed(tokens, |this, tokens| this.parse_expr(tokens))?;
Err(err) => {
tokens.advance_past_end_of_bracketed().ok_or(ErrorInfo {
error: ParseError::UnmatchedSquareBracket(
next.token_pos().start,
),
loc: tokens.current_source_location(),
})?;
self.push_error(err.error, err.loc)
}
};
return Ok(self return Ok(self
.ast .ast
@ -2919,7 +2812,6 @@ pub mod ast_gen {
let Some(tok) = tokens.peek_token() else { let Some(tok) = tokens.peek_token() else {
break; break;
}; };
eprintln!("maybe binop: {tok:?}");
let loc = tok.source_location(); let loc = tok.source_location();
let Some(prec) = PRECEDENCE_MAP.get(&tok.token()).cloned() else { let Some(prec) = PRECEDENCE_MAP.get(&tok.token()).cloned() else {
break; break;
@ -2931,7 +2823,6 @@ pub mod ast_gen {
// SAFETY: we peeked `tok` // SAFETY: we peeked `tok`
let tok = tokens.next().unwrap(); let tok = tokens.next().unwrap();
eprintln!("binop: {tok:?}");
let lhs = node; let lhs = node;
let rhs = self.parse_binary_expr(tokens, prec + 1)?; let rhs = self.parse_binary_expr(tokens, prec + 1)?;
@ -3029,30 +2920,7 @@ pub mod ast_gen {
let iff = tokens.eat_token(Token::If).unwrap(); let iff = tokens.eat_token(Token::If).unwrap();
let loc = iff.source_location(); let loc = iff.source_location();
let open_parens = tokens.eat_token(Token::OpenParens).ok_or(ErrorInfo { let cond = self.parse_parenthesised(tokens, |this, tokens| this.parse_expr(tokens))?;
error: ParseError::ExpectedToken(Token::OpenParens),
loc: tokens.current_source_location(),
})?;
let cond = match self.parse_expr(tokens) {
Ok(i) => {
let Some(_) = tokens.eat_token(Token::CloseParens) else {
return Err(ErrorInfo {
error: ParseError::UnmatchedParens(open_parens.token_pos().start),
loc: tokens.current_source_location(),
});
};
i
}
Err(err) => {
tokens.advance_past_end_of_parens().ok_or(ErrorInfo {
error: ParseError::UnmatchedParens(open_parens.token_pos().start),
loc: tokens.current_source_location(),
})?;
self.push_error(err.error, err.loc)
}
};
let body = self.parse_expr_or_block_as_block(tokens)?; let body = self.parse_expr_or_block_as_block(tokens)?;
@ -3100,6 +2968,73 @@ pub mod ast_gen {
} }
} }
/// TYPE_DECL <-
/// type IDENTIFIER = TYPE_UNION ;
/// type IDENTIFIER = '(' (TYPE,)* ')' ;
/// type IDENTIFIER = extern? union { (IDENTIFIER: TYPE,)* }
/// type IDENTIFIER = extern? packed? enum { (IDENTIFIER (= EXPRESSION),)* }
/// type IDENTIFIER = extern? packed? struct { (IDENTIFIER: TYPE,)* }
fn parse_type_decl(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
_ = tokens.eat_token(Token::Type).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Type),
loc: tokens.current_source_location(),
});
let name = self.parse_ident(tokens)?;
let loc = tokens.current_source_location();
_ = tokens.eat_token(Token::Equal).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Equal),
loc: tokens.current_source_location(),
});
let (has_attributes, c_like, packed) = {
let vec = tokens.eat_all_zero_or_once(&[Token::Extern, Token::Packed]);
(vec[0] || vec[1], vec[0], vec[1])
};
let Some(next) = tokens.peek_token() else {
return Err(ErrorInfo {
error: ParseError::ExpectedTypeDeclaration,
loc: tokens.current_source_location(),
});
};
match next.token() {
Token::Struct => self.parse_struct_decl(tokens, name, c_like, packed, loc),
Token::Union => {
unimplemented!()
}
Token::Enum => {
unimplemented!()
}
_ => {
if has_attributes {
return Err(ErrorInfo {
error: ParseError::UnexpectedTypeAttributes,
loc: tokens.current_source_location(),
});
}
match next.token() {
Token::OpenParens => {
// tuple
unimplemented!()
}
Token::Ident => {
// sumtype
unimplemented!()
}
_ => {
return Err(ErrorInfo {
error: ParseError::ExpectedTypeDeclaration,
loc: tokens.current_source_location(),
});
}
}
}
}
}
/// SUMTYPE_DECL <- /// SUMTYPE_DECL <-
/// type IDENTIFIER = TYPE_UNION /// type IDENTIFIER = TYPE_UNION
/// TYPE_UNION <- /// TYPE_UNION <-
@ -3129,48 +3064,30 @@ pub mod ast_gen {
/// STRUCT_DECL <- /// STRUCT_DECL <-
/// type IDENTIFIER = extern? packed? struct { STRUCT_FIELD,* } /// type IDENTIFIER = extern? packed? struct { STRUCT_FIELD,* }
fn parse_struct_decl(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> { fn parse_struct_decl(
&mut self,
tokens: &mut TokenIterator,
name: intern::Index,
c_like: bool,
packed: bool,
loc: SourceLocation,
) -> ParseResult<Index> {
// SAFETY: function invariance // SAFETY: function invariance
let start = tokens.eat_token(Token::Type).unwrap();
let loc = start.source_location();
let name = self.parse_ident(tokens)?;
_ = tokens.eat_token(Token::Equal).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Equal),
loc: tokens.current_source_location(),
})?;
let flags = tokens.eat_all_zero_or_once(&[Token::Packed, Token::Extern]);
let (packed, c_like) = (flags[0], flags[1]);
_ = tokens.eat_token(Token::Struct).ok_or(ErrorInfo { _ = tokens.eat_token(Token::Struct).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Struct), error: ParseError::ExpectedToken(Token::Struct),
loc: tokens.current_source_location(), loc: tokens.current_source_location(),
})?; })?;
_ = tokens.eat_token(Token::OpenBrace).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::OpenBrace),
loc: tokens.current_source_location(),
})?;
match self.parse_struct_fields(tokens) { let decl = self.parse_braced(tokens, |this, tokens| {
Ok(fields) => { this.parse_struct_fields(tokens).map(|fields| {
_ = tokens.eat_token(Token::Comma); _ = tokens.eat_token(Token::Comma);
let struct_type = this.intern.get_struct_type(name, packed, c_like, fields);
this.ast.push_struct_decl(struct_type, loc)
})
})?;
_ = tokens.eat_token(Token::CloseBrace).ok_or(ErrorInfo { Ok(decl)
error: ParseError::ExpectedToken(Token::CloseBrace),
loc: tokens.current_source_location(),
})?;
let struct_type = self.intern.get_struct_type(name, packed, c_like, fields);
return Ok(self.ast.push_struct_decl(struct_type, loc));
}
Err(err) => {
tokens.advance_past_end_of_braced().ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::CloseBrace),
loc: tokens.current_source_location(),
})?;
return Ok(self.push_error(err.error, err.loc));
}
}
} }
fn parse_with_trailing_semi<F>( fn parse_with_trailing_semi<F>(
@ -3283,6 +3200,18 @@ pub mod ast_gen {
}) })
} }
fn parse_bracketed<F>(&mut self, tokens: &mut TokenIterator, parse: F) -> ParseResult<Index>
where
F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult<Index>,
{
self.parse_inner2(
tokens,
Token::OpenSquareBracket,
Token::CloseSquareBracket,
parse,
)
}
fn parse_braced<F>(&mut self, tokens: &mut TokenIterator, parse: F) -> ParseResult<Index> fn parse_braced<F>(&mut self, tokens: &mut TokenIterator, parse: F) -> ParseResult<Index>
where where
F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult<Index>, F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult<Index>,
@ -3329,11 +3258,38 @@ pub mod ast_gen {
tokens: &mut TokenIterator, tokens: &mut TokenIterator,
) -> ParseResult<(intern::Index, intern::Index)> { ) -> ParseResult<(intern::Index, intern::Index)> {
let name = self.parse_ident(tokens)?; let name = self.parse_ident(tokens)?;
let Some(_) = tokens.eat_token(Token::Colon) else {
return Err(ErrorInfo {
error: ParseError::ExpectedToken(Token::Colon),
loc: tokens.current_source_location(),
});
};
let ty = self.parse_type(tokens)?; let ty = self.parse_type(tokens)?;
return Ok((name, ty)); return Ok((name, ty));
} }
/// CONSTANT_DECL <-
/// FUNCTION_DECL
/// GLOBAL_DECL
/// STRUCT_DECL
fn parse_constant_decls(
&mut self,
tokens: &mut TokenIterator,
) -> ParseResult<Option<Index>> {
let next = tokens.peek_token().ok_or(ErrorInfo {
error: ParseError::UnexpectedEndOfTokens,
loc: tokens.current_source_location(),
})?;
match next.token() {
Token::Fn => Ok(Some(self.parse_fn_decl(tokens))),
Token::Const => self.parse_const_decl(tokens).map(|i| Some(i)),
Token::Type => self.parse_type_decl(tokens).map(|i| Some(i)),
_ => Ok(None),
}
}
/// FILE <- /// FILE <-
/// (FUNCTION_DECL | GLOBAL_DECL)* /// (FUNCTION_DECL | GLOBAL_DECL)*
fn parse_file(&mut self, tokens: &mut TokenIterator) -> Index { fn parse_file(&mut self, tokens: &mut TokenIterator) -> Index {
@ -3343,25 +3299,22 @@ pub mod ast_gen {
self.push_scope(file, intern::Index::invalid()); self.push_scope(file, intern::Index::invalid());
while let Some(next) = tokens.peek_token() { while let Some(next) = tokens.peek_token() {
match next.token() { let loc = next.source_location();
Token::Fn => { let decl = match self.parse_constant_decls(tokens).and_then(|i| match i {
decls.push(self.parse_fn_decl(tokens)); Some(i) => Ok(i),
} None => {
Token::Const => {
decls.push(match self.parse_const_decl(tokens) {
Ok(i) => i,
Err(err) => self.push_error(err.error, err.loc),
});
}
_ => {
// error node:
let error = ParseError::UnexpectedTokenAtFileScope; let error = ParseError::UnexpectedTokenAtFileScope;
let node = self.push_error(error, next.source_location()); let node = self.push_error(error, loc);
decls.push(node);
self.find_next_fn_or_const(tokens); self.find_next_fn_or_const(tokens);
Ok(node)
} }
} }) {
Ok(i) => i,
Err(err) => self.push_error(err.error, err.loc),
};
decls.push(decl);
} }
self.pop_scope(); self.pop_scope();
@ -3437,7 +3390,7 @@ pub mod ast_gen {
fn find_next_fn_or_const(&mut self, tokens: &mut TokenIterator) -> Option<()> { fn find_next_fn_or_const(&mut self, tokens: &mut TokenIterator) -> Option<()> {
tokens tokens
.advance_until_before_one_of(&[Token::Const, Token::Fn]) .advance_until_before_one_of(&[Token::Const, Token::Fn, Token::Type])
.map(|_| ()) .map(|_| ())
} }
} }

View file

@ -7,6 +7,14 @@ use compiler::{
triples::{MirBuilder, IR}, triples::{MirBuilder, IR},
}; };
fn ast_tree(tokens: &Tokenizer) -> Tree {
let mut tree = Tree::new();
tree.parse(tokens.iter()).unwrap();
tree.fold_comptime();
tree
}
fn main() { fn main() {
let cmd = clap::Command::new("sea") let cmd = clap::Command::new("sea")
.bin_name("sea") .bin_name("sea")
@ -37,13 +45,10 @@ fn main() {
let tokens = Tokenizer::new(&source).unwrap(); let tokens = Tokenizer::new(&source).unwrap();
let mut tree = Tree::new();
tree.parse(tokens.iter()).unwrap();
tree.fold_comptime();
if let Some((cmd, _matches)) = matches.subcommand() { if let Some((cmd, _matches)) = matches.subcommand() {
match cmd { match cmd {
"ast" => { "ast" => {
let mut tree = ast_tree(&tokens);
let mut buf = String::new(); let mut buf = String::new();
tree.render(&mut buf).unwrap(); tree.render(&mut buf).unwrap();
println!("AST:\n{buf}"); println!("AST:\n{buf}");
@ -55,6 +60,7 @@ fn main() {
println!("AST (new):\n{tree2}"); println!("AST (new):\n{tree2}");
} }
"ir" => { "ir" => {
let mut tree = ast_tree(&tokens);
let mut ir = IR::new(); let mut ir = IR::new();
let builder = ir.build(&mut tree); let builder = ir.build(&mut tree);
let mut buf = String::new(); let mut buf = String::new();
@ -62,6 +68,7 @@ fn main() {
println!("IR:\n{buf}"); println!("IR:\n{buf}");
} }
"mir" => { "mir" => {
let mut tree = ast_tree(&tokens);
let mut ir = IR::new(); let mut ir = IR::new();
ir.build(&mut tree); ir.build(&mut tree);
@ -77,6 +84,7 @@ fn main() {
} }
} }
"asm" => { "asm" => {
let mut tree = ast_tree(&tokens);
let mut ir = IR::new(); let mut ir = IR::new();
ir.build(&mut tree); ir.build(&mut tree);

8
tests/legal/struct.sea Normal file
View file

@ -0,0 +1,8 @@
type MyStruct = struct {
i: i32,
b: bool,
}
fn square_if_true(arg: MyStruct) -> i32 {
0
}