aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-10-18 09:25:36 +0200
committerEugen Wissner <belka@caraus.de>2026-10-18 22:54:00 +0200
commit7440060f179e35fe63155a149bb5003c97e12013 (patch)
treeabdcb8aa74513312b2cc92d4c2b2b986c29fbc03
parent81043b67437445dcf83055098490e04cc88ff7ee (diff)
downloadelna-7440060f179e35fe63155a149bb5003c97e12013.tar.gz
Fix ICE on variable declarations in case branches
-rw-r--r--boot/ast.cc29
-rw-r--r--boot/name_analysis.cc3
-rw-r--r--boot/type_check.cc42
-rw-r--r--include/elna/boot/ast.h4
-rw-r--r--include/elna/boot/type_check.h6
-rw-r--r--source/elna/lexer.elna128
-rw-r--r--source/main.elna4
-rw-r--r--source/sys/posix/stdlib.elna3
-rw-r--r--source/sys/posix/string.elna3
9 files changed, 152 insertions, 70 deletions
diff --git a/boot/ast.cc b/boot/ast.cc
index 15e4928..e712f16 100644
--- a/boot/ast.cc
+++ b/boot/ast.cc
@@ -243,7 +243,7 @@ namespace elna::boot
}
if (declaration->body.has_value())
{
- traverse_block(this, declaration->body.value());
+ boot::traverse_block(this, declaration->body.value());
if (declaration->body.value().return_expression != nullptr)
{
declaration->body.value().return_expression->accept(this);
@@ -257,54 +257,59 @@ namespace elna::boot
statement->rvalue().accept(this);
}
+ void walking_visitor::traverse_block(const block& body)
+ {
+ boot::traverse_block(this, body);
+ }
+
void walking_visitor::visit(if_statement *statement)
{
statement->branch().prerequisite().accept(this);
- traverse_block(this, statement->branch().body);
+ traverse_block(statement->branch().body);
for (conditional_statements& branch : statement->branches)
{
branch.prerequisite().accept(this);
- traverse_block(this, branch.body);
+ traverse_block(branch.body);
}
if (statement->alternative != nullptr)
{
- traverse_block(this, *statement->alternative);
+ traverse_block(*statement->alternative);
}
}
void walking_visitor::visit(while_statement *statement)
{
statement->branch().prerequisite().accept(this);
- traverse_block(this, statement->branch().body);
+ traverse_block(statement->branch().body);
for (conditional_statements& branch : statement->branches)
{
branch.prerequisite().accept(this);
- traverse_block(this, branch.body);
+ traverse_block(branch.body);
}
}
void walking_visitor::visit(repeat_statement *statement)
{
statement->condition().accept(this);
- traverse_block(this, statement->body);
+ traverse_block(statement->body);
}
void walking_visitor::visit(for_statement *statement)
{
statement->range().accept(this);
- traverse_block(this, statement->body);
+ traverse_block(statement->body);
}
void walking_visitor::visit(defer_statement *statement)
{
- traverse_block(this, statement->body);
+ traverse_block(statement->body);
}
void walking_visitor::visit(block_statement *statement)
{
- traverse_block(this, statement->body);
+ traverse_block(statement->body);
}
void walking_visitor::visit(break_statement *)
@@ -321,11 +326,11 @@ namespace elna::boot
for (const switch_case& case_block : statement->cases)
{
visit_all(this, case_block.labels);
- traverse_block(this, case_block.body);
+ traverse_block(case_block.body);
}
if (statement->alternative != nullptr)
{
- traverse_block(this, *statement->alternative);
+ traverse_block(*statement->alternative);
}
}
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 9305cd2..11380db 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -1628,7 +1628,8 @@ namespace elna::boot
this->bag.leave();
info_object->parameters = type_parameters;
info_object->exported = declaration->identifier.exported();
- info_object->position.emplace(declaration->position());
+ info_object->position.emplace(declaration->position().start(),
+ declaration->heading().position().end());
info_object->file = this->module_file;
info_object->noreturn = noreturn;
diff --git a/boot/type_check.cc b/boot/type_check.cc
index 5a8170a..5033ffd 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -494,9 +494,8 @@ namespace elna::boot
return is_assignable_from(left, right) || is_assignable_from(right, left);
}
- void type_analysis_visitor::visit_and_validate_condition(expression& condition)
+ void type_analysis_visitor::validate_condition(const expression& condition)
{
- condition.accept(this);
if (!is_primitive_type(condition.type_decoration, "Bool"))
{
add_error<type_requirement_error>(condition.position(),
@@ -824,7 +823,7 @@ namespace elna::boot
void type_analysis_visitor::visit(for_statement *statement)
{
- statement->range().accept(this);
+ walking_visitor::visit(statement);
const type resolved_range = resolve_underlying_type(statement->range().type_decoration);
if (!get_range_base_type(resolved_range))
@@ -832,47 +831,40 @@ namespace elna::boot
add_error<type_requirement_error>(statement->range().position(),
statement->range().type_decoration, type_requirement_error::kind::for_range);
}
- traverse_block(statement->body);
}
- void type_analysis_visitor::traverse_block(block& body)
+ void type_analysis_visitor::traverse_block(const block& body)
{
this->bag.enter(body.symbols);
- boot::traverse_block(this, body);
+ walking_visitor::traverse_block(body);
this->bag.leave();
}
void type_analysis_visitor::visit(repeat_statement *statement)
{
- visit_and_validate_condition(statement->condition());
- traverse_block(statement->body);
+ walking_visitor::visit(statement);
+ validate_condition(statement->condition());
}
void type_analysis_visitor::visit(while_statement *statement)
{
- visit_and_validate_condition(statement->branch().prerequisite());
- traverse_block(statement->branch().body);
+ walking_visitor::visit(statement);
+ validate_condition(statement->branch().prerequisite());
for (conditional_statements& branch : statement->branches)
{
- visit_and_validate_condition(branch.prerequisite());
- traverse_block(branch.body);
+ validate_condition(branch.prerequisite());
}
}
void type_analysis_visitor::visit(if_statement *statement)
{
- visit_and_validate_condition(statement->branch().prerequisite());
- traverse_block(statement->branch().body);
+ walking_visitor::visit(statement);
+ validate_condition(statement->branch().prerequisite());
for (conditional_statements& branch : statement->branches)
{
- visit_and_validate_condition(branch.prerequisite());
- traverse_block(branch.body);
- }
- if (statement->alternative != nullptr)
- {
- traverse_block(*statement->alternative);
+ validate_condition(branch.prerequisite());
}
}
@@ -1462,16 +1454,6 @@ namespace elna::boot
}
// else callable is not declared, or inference failed and reported.
}
-
- void type_analysis_visitor::visit(defer_statement *statement)
- {
- traverse_block(statement->body);
- }
-
- void type_analysis_visitor::visit(block_statement *statement)
- {
- traverse_block(statement->body);
- }
void type_analysis_visitor::visit(record_constructor_expression *expression)
{
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index c12d8e8..cf381fc 100644
--- a/include/elna/boot/ast.h
+++ b/include/elna/boot/ast.h
@@ -322,6 +322,10 @@ namespace elna::boot
void visit(literal<bool> *) override;
void visit(literal<std::uint32_t> *) override;
void visit(literal<std::string> *) override;
+
+ protected:
+ /// Passes needing the scope of a block override this.
+ virtual void traverse_block(const block& body);
};
/**
diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h
index 98db843..3774311 100644
--- a/include/elna/boot/type_check.h
+++ b/include/elna/boot/type_check.h
@@ -276,7 +276,7 @@ namespace elna::boot
static bool is_assignable_from(const type& assignee, const type& assignment);
static bool is_equality_compatible(const type& left, const type& right);
- void visit_and_validate_condition(expression& condition);
+ void validate_condition(const expression& condition);
void check_arguments(const named_expression& reference, const std::vector<type>& arguments,
const argument_rule rule);
void check_parameters_used(const source_position position, const std::string& name,
@@ -292,7 +292,7 @@ namespace elna::boot
type promote_variadic_argument(const expression& argument);
protected:
- void traverse_block(block& body);
+ void traverse_block(const block& body) override;
public:
explicit type_analysis_visitor(symbol_bag bag, const target_info& target);
@@ -309,8 +309,6 @@ namespace elna::boot
void visit(repeat_statement *statement) override;
void visit(while_statement *statement) override;
void visit(if_statement *statement) override;
- void visit(defer_statement *statement) override;
- void visit(block_statement *statement) override;
void visit(record_constructor_expression *expression) override;
void visit(array_constructor_expression *expression) override;
void visit(traits_expression *trait) override;
diff --git a/source/elna/lexer.elna b/source/elna/lexer.elna
index 64fcc22..4de178e 100644
--- a/source/elna/lexer.elna
+++ b/source/elna/lexer.elna
@@ -139,6 +139,26 @@ type
trait,
(* Token is complete, the last byte eaten. *)
stop,
+ (* One-character token stop. *)
+ single,
+ (* Assignment operator stop. *)
+ assignment,
+ (* Arrow stop. *)
+ arrow,
+ (* Greater or equal stop. *)
+ greater_equal,
+ (* Less or equal stop. *)
+ less_equal,
+ (* Unequal stop. *)
+ not_equal,
+ (* Right shift stop. *)
+ shift_right,
+ (* Left shift stop. *)
+ shift_left,
+ (* Opening attribute bracket stop. *)
+ attribute_open,
+ (* Opening generic brace stop. *)
+ generic_open,
(* Reject the token with an error. *)
reject,
(* Sentinel signaling end of input. *)
@@ -241,6 +261,9 @@ type
kind: ElnaLexerTokenKind;
position: ElnaSourceSpan
end
+ ElnaLexerIdentifierToken = record(ElnaLexerToken)
+ identifier: StdString
+ end
var
(* Initializes the array with character classes. *)
@@ -554,17 +577,17 @@ begin
elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.space, ElnaLexerState.skip);
elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.new_line, ElnaLexerState.skip);
elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.colon, ElnaLexerState.colon);
- elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.equals, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.equals, ElnaLexerState.single);
elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.left_paren, ElnaLexerState.left_paren);
- elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.right_paren, ElnaLexerState.stop);
- elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.asterisk, ElnaLexerState.stop);
- elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.single, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.right_paren, ElnaLexerState.single);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.asterisk, ElnaLexerState.single);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.single, ElnaLexerState.single);
elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.backquote, ElnaLexerState.wide);
- elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.brace, ElnaLexerState.stop);
- elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.square, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.brace, ElnaLexerState.single);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.square, ElnaLexerState.single);
elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.eof, ElnaLexerState.eof);
- elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.dot, ElnaLexerState.stop);
- elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.plus, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.dot, ElnaLexerState.single);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.plus, ElnaLexerState.single);
elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.minus, ElnaLexerState.minus);
elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.single_quote, ElnaLexerState.narrow);
elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.double_quote, ElnaLexerState.string);
@@ -583,23 +606,23 @@ begin
elna_lexer_set_transition(ElnaLexerState.skip, ElnaLexerClass.new_line, ElnaLexerState.skip);
-- Colon state.
- elna_lexer_set_transition(ElnaLexerState.colon, ElnaLexerClass.equals, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.colon, ElnaLexerClass.equals, ElnaLexerState.assignment);
-- Left paren state.
elna_lexer_set_transition(ElnaLexerState.left_paren, ElnaLexerClass.asterisk, ElnaLexerState.docblock);
-- Minus state.
- elna_lexer_set_transition(ElnaLexerState.minus, ElnaLexerClass.greater, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.minus, ElnaLexerClass.greater, ElnaLexerState.arrow);
elna_lexer_set_transition(ElnaLexerState.minus, ElnaLexerClass.minus, ElnaLexerState.comment);
-- Greater state.
- elna_lexer_set_transition(ElnaLexerState.greater, ElnaLexerClass.greater, ElnaLexerState.stop);
- elna_lexer_set_transition(ElnaLexerState.greater, ElnaLexerClass.equals, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.greater, ElnaLexerClass.greater, ElnaLexerState.shift_right);
+ elna_lexer_set_transition(ElnaLexerState.greater, ElnaLexerClass.equals, ElnaLexerState.greater_equal);
-- Less state.
- elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.less, ElnaLexerState.stop);
- elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.equals, ElnaLexerState.stop);
- elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.greater, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.less, ElnaLexerState.shift_left);
+ elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.equals, ElnaLexerState.less_equal);
+ elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.greater, ElnaLexerState.not_equal);
-- Numbers prefixed by 0 followed by a character (0b, 0x).
elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.zero, ElnaLexerState.integer);
@@ -666,8 +689,8 @@ begin
-- Attribute, generic parameter or trait marker ('#').
elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.exponent, ElnaLexerState.trait);
elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.alpha, ElnaLexerState.trait);
- elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.square, ElnaLexerState.stop);
- elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.brace, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.square, ElnaLexerState.generic_open);
+ elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.brace, ElnaLexerState.attribute_open);
-- Trait identifier.
elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.exponent, ElnaLexerState.trait);
@@ -751,16 +774,32 @@ proc elna_lexer_skip_trivia(cursor: ^ElnaLexerCursor)
until cursor^.state <> ElnaLexerState.skip
return
+(*
+ Creates a token of the given kind. The created token is a simple token without
+ any payload.
+*)
proc elna_lexer_token_create(kind: ElnaLexerTokenKind) -> ^ElnaLexerToken
var
result: ^ElnaLexerToken
begin
result := malloc#[ElnaLexerToken](#size(ElnaLexerToken));
- memset(result, 0i8, #size(ElnaLexerToken));
result^.kind := kind
return result
--- TODO: Identifier (else) should save a payload with the identifier.
+(*
+ Frees the memory allocated for the token itself and for its internal storage.
+*)
+proc elna_lexer_token_destroy*(token: ^ElnaLexerToken)
+ if token^.kind = ElnaLexerTokenKind.identifier then
+ var
+ identifier_token := cast(token: ^ElnaLexerIdentifierToken)
+ begin
+ free(identifier_token^.identifier.ptr)
+ end;
+
+ free(token)
+return
+
proc elna_lexer_identify_keyword(start, cursor: ^const Word8) -> ^ElnaLexerToken
var
result: ^ElnaLexerToken
@@ -804,7 +843,43 @@ begin
"program": result := elna_lexer_token_create(ElnaLexerTokenKind.program_) |
"module": result := elna_lexer_token_create(ElnaLexerTokenKind.module_)
else
- result := elna_lexer_token_create(ElnaLexerTokenKind.identifier)
+ var
+ identifier_token := malloc#[ElnaLexerIdentifierToken](#size(ElnaLexerToken))
+ identifier_pointer := malloc#[const Word8](identifier.length)
+ begin
+ memcpy(identifier_pointer, identifier.ptr, identifier.length);
+
+ identifier_token^.kind := ElnaLexerTokenKind.identifier;
+ identifier_token^.identifier := identifier_pointer[1 to identifier.length];;
+ result := identifier_token
+ end
+return result
+
+proc elna_lexer_stop_single(start: ^const Word8) -> ^ElnaLexerToken
+var
+ result: ^ElnaLexerToken
+begin
+ case start^ of
+ '&': result := elna_lexer_token_create(ElnaLexerTokenKind.and) |
+ '|': result := elna_lexer_token_create(ElnaLexerTokenKind.pipe) |
+ '~': result := elna_lexer_token_create(ElnaLexerTokenKind.not) |
+ '/': result := elna_lexer_token_create(ElnaLexerTokenKind.division) |
+ ')': result := elna_lexer_token_create(ElnaLexerTokenKind.right_paren) |
+ '[': result := elna_lexer_token_create(ElnaLexerTokenKind.left_square) |
+ ']': result := elna_lexer_token_create(ElnaLexerTokenKind.right_square) |
+ '{': result := elna_lexer_token_create(ElnaLexerTokenKind.left_brace) |
+ '}': result := elna_lexer_token_create(ElnaLexerTokenKind.right_brace) |
+ '=': result := elna_lexer_token_create(ElnaLexerTokenKind.equals) |
+ ';': result := elna_lexer_token_create(ElnaLexerTokenKind.semicolon) |
+ '.': result := elna_lexer_token_create(ElnaLexerTokenKind.dot) |
+ ',': result := elna_lexer_token_create(ElnaLexerTokenKind.comma) |
+ '+': result := elna_lexer_token_create(ElnaLexerTokenKind.plus) |
+ '*': result := elna_lexer_token_create(ElnaLexerTokenKind.multiplication) |
+ '%': result := elna_lexer_token_create(ElnaLexerTokenKind.remainder) |
+ '^': result := elna_lexer_token_create(ElnaLexerTokenKind.hat) |
+ '@': result := elna_lexer_token_create(ElnaLexerTokenKind.at)
+ else
+ assert(false)
end
return result
@@ -816,12 +891,21 @@ begin
elna_lexer_skip_trivia(cursor);
case cursor^.state of
- ElnaLexerState.identifier: result := elna_lexer_identify_keyword(cursor^.start, cursor^.cursor)
+ ElnaLexerState.identifier: result := elna_lexer_identify_keyword(cursor^.start, cursor^.cursor) |
+ ElnaLexerState.single: result := elna_lexer_stop_single(cursor^.start) |
+ ElnaLexerState.attribute_open: result := elna_lexer_token_create(ElnaLexerTokenKind.attribute_open) |
+ ElnaLexerState.generic_open: result := elna_lexer_token_create(ElnaLexerTokenKind.generic_open) |
+ ElnaLexerState.shift_left: result := elna_lexer_token_create(ElnaLexerTokenKind.shift_left) |
+ ElnaLexerState.shift_right: result := elna_lexer_token_create(ElnaLexerTokenKind.shift_right) |
+ ElnaLexerState.greater_equal: result := elna_lexer_token_create(ElnaLexerTokenKind.greater_equal) |
+ ElnaLexerState.less_equal: result := elna_lexer_token_create(ElnaLexerTokenKind.less_equal) |
+ ElnaLexerState.not_equal: result := elna_lexer_token_create(ElnaLexerTokenKind.not_equal) |
+ ElnaLexerState.assignment: result := elna_lexer_token_create(ElnaLexerTokenKind.assignment) |
+ ElnaLexerState.arrow: result := elna_lexer_token_create(ElnaLexerTokenKind.arrow)
else
result := malloc#[ElnaLexerToken](#size(ElnaLexerToken));
memset(result, 0i8, #size(ElnaLexerToken))
end
-
return result
end.
diff --git a/source/main.elna b/source/main.elna
index ee4079e..fb58766 100644
--- a/source/main.elna
+++ b/source/main.elna
@@ -23,6 +23,8 @@ begin
printf("Token: %.*s, length: %lu, final state: %i, position: %lu, kind: %i\n\0".ptr,
global_cursor.cursor - global_cursor.start, global_cursor.start,
global_cursor.cursor - global_cursor.start, global_cursor.state,
- global_cursor.position, token^.kind)
+ global_cursor.position, token^.kind);
+
+ elna_lexer_token_destroy(token)
until global_cursor.state = ElnaLexerState.eof
end.
diff --git a/source/sys/posix/stdlib.elna b/source/sys/posix/stdlib.elna
index 0016d9c..66b383a 100644
--- a/source/sys/posix/stdlib.elna
+++ b/source/sys/posix/stdlib.elna
@@ -6,4 +6,7 @@ module
proc malloc#[T]*(size: Word) -> ^T
extern
+proc free#[T]*(ptr: ^T)
+extern
+
end.
diff --git a/source/sys/posix/string.elna b/source/sys/posix/string.elna
index 9994679..b699606 100644
--- a/source/sys/posix/string.elna
+++ b/source/sys/posix/string.elna
@@ -6,4 +6,7 @@ module
proc memset#[T]*(b: ^T; c: Int8; len: Word) -> ^T
extern
+proc memcpy#[T]*(dst: ^T; src: ^const T; n: Word) -> ^T
+extern
+
end.