diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-07-15 17:23:42 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-07-15 17:23:42 +0200 |
| commit | e7265af15f77abf60a899b3d53a59652b5ca71ea (patch) | |
| tree | 50b5594c4517da87b185268e709194e964c236af | |
| parent | 308a2addf655fe45f2098f8542f0ff1290612cd9 (diff) | |
| download | elna-e7265af15f77abf60a899b3d53a59652b5ca71ea.tar.gz | |
Track identifier positions for the diagnostics
| -rw-r--r-- | boot/ast.cc | 142 | ||||
| -rw-r--r-- | boot/driver.cc | 4 | ||||
| -rw-r--r-- | boot/parser.yy | 53 | ||||
| -rw-r--r-- | boot/result.cc | 107 | ||||
| -rw-r--r-- | boot/semantic.cc | 331 | ||||
| -rw-r--r-- | boot/symbol.cc | 48 | ||||
| -rw-r--r-- | gcc/gcc/elna-diagnostic.cc | 38 | ||||
| -rw-r--r-- | gcc/gcc/elna-generic.cc | 45 | ||||
| -rw-r--r-- | include/elna/boot/ast.h | 117 | ||||
| -rw-r--r-- | include/elna/boot/driver.h | 2 | ||||
| -rw-r--r-- | include/elna/boot/result.h | 97 | ||||
| -rw-r--r-- | include/elna/boot/semantic.h | 112 | ||||
| -rw-r--r-- | include/elna/boot/symbol.h | 7 | ||||
| -rw-r--r-- | include/elna/gcc/elna-diagnostic.h | 4 |
14 files changed, 770 insertions, 337 deletions
diff --git a/boot/ast.cc b/boot/ast.cc index 2a22d69..37e89ac 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -413,7 +413,7 @@ namespace elna::boot { for (const field_initializer& initializer : expression->field_initializers) { - initializer.value->accept(this); + initializer.value().accept(this); } } @@ -493,8 +493,8 @@ namespace elna::boot { } - node::node(const struct position position) - : source_position(position) + node::node(const source_position position) + : m_position(position) { } @@ -502,9 +502,9 @@ namespace elna::boot { } - const struct position& node::position() const + const source_position& node::position() const { - return this->source_position; + return this->m_position; } cast_expression *expression::is_cast() @@ -572,7 +572,7 @@ namespace elna::boot return nullptr; } - array_type_expression::array_type_expression(const struct position position, + array_type_expression::array_type_expression(const source_position position, type_expression *base, const std::uint32_t size) : node(position), m_base(base), size(size) { @@ -598,7 +598,7 @@ namespace elna::boot return *m_base; } - pointer_type_expression::pointer_type_expression(const struct position position, + pointer_type_expression::pointer_type_expression(const source_position position, type_expression *base) : node(position), m_base(base) { @@ -624,15 +624,15 @@ namespace elna::boot return *m_base; } - record_type_expression::record_type_expression(const struct position position, + record_type_expression::record_type_expression(const source_position position, std::vector<field_declaration>&& fields) : node(position), fields(std::move(fields)) { } - record_type_expression::record_type_expression(const struct position position, - std::vector<field_declaration>&& fields, std::string&& base) - : node(position), fields(std::move(fields)), base(std::make_optional<std::string>(std::move(base))) + record_type_expression::record_type_expression(const source_position position, + std::vector<field_declaration>&& fields, identifier&& base) + : node(position), fields(std::move(fields)), base(std::make_optional<identifier>(std::move(base))) { } @@ -646,8 +646,48 @@ namespace elna::boot return this; } - record_constructor_expression::record_constructor_expression(const struct position position, - std::string&& type_name, + field_initializer::field_initializer(identifier name, expression *value) + : m_name(std::move(name)), m_value(value) + { + } + + field_initializer::~field_initializer() + { + delete this->m_value; + } + + field_initializer::field_initializer(field_initializer&& other) noexcept + : m_name(std::move(other.m_name)), m_value(other.m_value) + { + other.m_value = nullptr; + } + + field_initializer& field_initializer::operator=(field_initializer&& other) noexcept + { + delete this->m_value; + this->m_name = std::move(other.m_name); + this->m_value = other.m_value; + other.m_value = nullptr; + return *this; + } + + const std::string& field_initializer::name() const + { + return this->m_name.name(); + } + + const identifier& field_initializer::id() const + { + return this->m_name; + } + + expression& field_initializer::value() const + { + return *this->m_value; + } + + record_constructor_expression::record_constructor_expression(const source_position position, + identifier&& type_name, std::vector<field_initializer>&& field_initializers) : node(position), type_name(std::move(type_name)), field_initializers(std::move(field_initializers)) { @@ -658,15 +698,7 @@ namespace elna::boot visitor->visit(this); } - record_constructor_expression::~record_constructor_expression() - { - for (const field_initializer& initializer : field_initializers) - { - delete initializer.value; - } - } - - array_constructor_expression::array_constructor_expression(const struct position position, + array_constructor_expression::array_constructor_expression(const source_position position, std::uint32_t size, type_expression *element_type, std::vector<expression *>&& elements) : node(position), size(size), m_element_type(element_type), elements(std::move(elements)) @@ -687,14 +719,14 @@ namespace elna::boot } } - variable_declaration::variable_declaration(const struct position position, + variable_declaration::variable_declaration(const source_position position, std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type, expression *initializer) : node(position), m_variable_type(variable_type), identifiers(std::move(identifier)), initializer(initializer) { } - variable_declaration::variable_declaration(const struct position position, + variable_declaration::variable_declaration(const source_position position, std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type, std::monostate) : node(position), m_variable_type(variable_type), identifiers(std::move(identifier)), is_extern(true) @@ -716,12 +748,12 @@ namespace elna::boot return *m_variable_type; } - declaration::declaration(const struct position position, identifier_definition identifier) + declaration::declaration(const source_position position, identifier_definition identifier) : node(position), identifier(identifier) { } - constant_declaration::constant_declaration(const struct position position, identifier_definition identifier, + constant_declaration::constant_declaration(const source_position position, identifier_definition identifier, expression *initializer) : declaration(position, identifier), m_initializer(initializer) { @@ -742,7 +774,7 @@ namespace elna::boot delete m_initializer; } - procedure_type_expression::procedure_type_expression(const struct position position, + procedure_type_expression::procedure_type_expression(const source_position position, std::vector<field_declaration>&& parameters, return_t return_type) : node(position), return_type(return_type), parameters(std::move(parameters)) { @@ -766,9 +798,9 @@ namespace elna::boot return this; } - enumeration_type_expression::enumeration_type_expression(const struct position position, - std::vector<std::string>&& members) - : node(position), members(members) + enumeration_type_expression::enumeration_type_expression(const source_position position, + std::vector<identifier>&& members) + : node(position), members(std::move(members)) { } @@ -782,14 +814,14 @@ namespace elna::boot return this; } - procedure_declaration::procedure_declaration(const struct position position, identifier_definition identifier, + procedure_declaration::procedure_declaration(const source_position position, identifier_definition identifier, procedure_type_expression *heading, procedure_body&& body) : declaration(position, identifier), m_heading(heading), body(std::make_optional<procedure_body>(std::move(body))) { } - procedure_declaration::procedure_declaration(const struct position position, identifier_definition identifier, + procedure_declaration::procedure_declaration(const source_position position, identifier_definition identifier, procedure_type_expression *heading) : declaration(position, identifier), m_heading(heading) { @@ -810,7 +842,7 @@ namespace elna::boot delete m_heading; } - type_declaration::type_declaration(const struct position position, identifier_definition identifier, + type_declaration::type_declaration(const source_position position, identifier_definition identifier, type_expression *underlying_type) : declaration(position, identifier), m_underlying_type(underlying_type) { @@ -863,13 +895,13 @@ namespace elna::boot } } - unit::unit(const struct position position) + unit::unit(const source_position position) : node(position), procedure_body(std::vector<constant_declaration *>{}, std::vector<variable_declaration *>{}, std::vector<statement *>{}, nullptr) { } - unit::unit(const struct position position, + unit::unit(const source_position position, std::vector<import_declaration *>&& imports, std::vector<constant_declaration *>&& constants, std::vector<type_declaration *>&& types, @@ -913,7 +945,7 @@ namespace elna::boot return this; } - defer_statement::defer_statement(const struct position position, std::vector<statement *>&& statements) + defer_statement::defer_statement(const source_position position, std::vector<statement *>&& statements) : node(position), statements(std::move(statements)) { } @@ -936,7 +968,7 @@ namespace elna::boot visitor->visit(this); } - empty_statement::empty_statement(const struct position position) + empty_statement::empty_statement(const source_position position) : node(position) { } @@ -971,7 +1003,7 @@ namespace elna::boot __builtin_unreachable(); } - named_expression::named_expression(const struct position position, const std::string& name) + named_expression::named_expression(const source_position position, const std::string& name) : node(position), name(name) { } @@ -986,7 +1018,7 @@ namespace elna::boot return this; } - array_access_expression::array_access_expression(const struct position position, + array_access_expression::array_access_expression(const source_position position, expression *base, expression *index) : node(position), m_base(base), m_index(index) { @@ -1018,9 +1050,9 @@ namespace elna::boot delete m_base; } - field_access_expression::field_access_expression(const struct position position, - expression *base, const std::string& field) - : node(position), m_base(base), m_field(field) + field_access_expression::field_access_expression(const source_position position, + expression *base, identifier&& field) + : node(position), m_base(base), m_field(std::move(field)) { } @@ -1034,7 +1066,7 @@ namespace elna::boot return *m_base; } - std::string& field_access_expression::field() + const identifier& field_access_expression::field() { return m_field; } @@ -1049,7 +1081,7 @@ namespace elna::boot delete m_base; } - dereference_expression::dereference_expression(const struct position position, + dereference_expression::dereference_expression(const source_position position, expression *base) : node(position), m_base(base) { @@ -1075,7 +1107,7 @@ namespace elna::boot delete m_base; } - binary_expression::binary_expression(const struct position position, expression *lhs, + binary_expression::binary_expression(const source_position position, expression *lhs, expression *rhs, const binary_operator operation) : node(position), m_lhs(lhs), m_rhs(rhs), m_operator(operation) { @@ -1112,7 +1144,7 @@ namespace elna::boot delete m_rhs; } - unary_expression::unary_expression(const struct position position, expression *operand, + unary_expression::unary_expression(const source_position position, expression *operand, const unary_operator operation) : node(position), m_operand(std::move(operand)), m_operator(operation) { @@ -1143,7 +1175,7 @@ namespace elna::boot delete m_operand; } - procedure_call::procedure_call(const struct position position, designator_expression *callable, + procedure_call::procedure_call(const source_position position, designator_expression *callable, std::vector<expression *>&& arguments) : node(position), m_callable(callable), arguments(std::move(arguments)) { @@ -1173,7 +1205,7 @@ namespace elna::boot delete m_callable; } - cast_expression::cast_expression(const struct position position, type_expression *target, expression *value) + cast_expression::cast_expression(const source_position position, type_expression *target, expression *value) : node(position), m_target(target), m_value(value) { } @@ -1204,9 +1236,9 @@ namespace elna::boot delete m_value; } - traits_expression::traits_expression(const struct position position, const std::string& name, + traits_expression::traits_expression(const source_position position, identifier&& name, std::vector<type_expression *>&& arguments) - : node(position), arguments(std::move(arguments)), name(name) + : node(position), arguments(std::move(arguments)), name(std::move(name)) { } @@ -1247,7 +1279,7 @@ namespace elna::boot } } - case_statement::case_statement(const struct position position, + case_statement::case_statement(const source_position position, expression *condition, std::vector<switch_case>&& cases, std::vector<statement *> *alternative) : node(position), m_condition(condition), cases(std::move(cases)), alternative(alternative) { @@ -1263,7 +1295,7 @@ namespace elna::boot return *m_condition; } - assign_statement::assign_statement(const struct position position, designator_expression *lvalue, + assign_statement::assign_statement(const source_position position, designator_expression *lvalue, expression *rvalue) : node(position), m_lvalue(lvalue), m_rvalue(rvalue) { @@ -1309,7 +1341,7 @@ namespace elna::boot delete m_rvalue; } - if_statement::if_statement(const struct position position, conditional_statements *branch, + if_statement::if_statement(const source_position position, conditional_statements *branch, std::vector<conditional_statements *>&& branches, std::vector<statement *> *alternative) : node(position), m_branch(branch), branches(std::move(branches)), alternative(alternative) @@ -1336,7 +1368,7 @@ namespace elna::boot delete this->alternative; } - import_declaration::import_declaration(const struct position position, std::vector<std::string>&& segments) + import_declaration::import_declaration(const source_position position, std::vector<std::string>&& segments) : node(position), segments(std::move(segments)) { } @@ -1346,7 +1378,7 @@ namespace elna::boot visitor->visit(this); } - while_statement::while_statement(const struct position position, conditional_statements *branch, + while_statement::while_statement(const source_position position, conditional_statements *branch, std::vector<conditional_statements *>&& branches) : node(position), m_branch(branch), branches(std::move(branches)) { diff --git a/boot/driver.cc b/boot/driver.cc index b51da32..8dc95f8 100644 --- a/boot/driver.cc +++ b/boot/driver.cc @@ -19,14 +19,14 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { - position make_position(const yy::location& location) + source_position make_position(const yy::location& location) { auto start_location = boot::location(static_cast<std::size_t>(location.begin.line), static_cast<std::size_t>(location.begin.column)); auto end_location = boot::location(static_cast<std::size_t>(location.end.line), static_cast<std::size_t>(location.end.column)); - return position(start_location, end_location); + return source_position(start_location, end_location); } syntax_error::syntax_error(const std::string& message, const yy::location& location) diff --git a/boot/parser.yy b/boot/parser.yy index 9f610ee..c34c905 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -153,14 +153,16 @@ along with GCC; see the file COPYING3. If not see %type <std::unique_ptr<elna::boot::procedure_body>> procedure_body; %type <elna::boot::field_declaration> field_declaration; %type <std::vector<elna::boot::field_declaration>> optional_fields required_fields; -%type <elna::boot::field_initializer> field_initializer; +%type <std::unique_ptr<elna::boot::field_initializer>> field_initializer; %type <std::vector<elna::boot::field_initializer>> field_initializers; %type <std::vector<elna::boot::conditional_statements *>> elsif_then_statements elsif_do_statements; %type <std::vector<elna::boot::statement *> *> else_statements; %type <elna::boot::cast_expression *> cast_expression; -%type <elna::boot::identifier_definition> identifier_definition; +%type <std::unique_ptr<elna::boot::identifier>> identifier; +%type <std::unique_ptr<elna::boot::identifier_definition>> identifier_definition; %type <std::vector<elna::boot::identifier_definition>> identifier_definitions; -%type <std::vector<std::string>> identifiers import_declaration; +%type <std::vector<std::string>> import_declaration; +%type <std::vector<elna::boot::identifier>> identifiers; %type <std::vector<elna::boot::import_declaration *>> import_declarations import_part; %% program: @@ -176,16 +178,18 @@ procedure_body: statement_part: /* no statements */ {} | "begin" statements { $$ = $2; } +identifier: + IDENTIFIER { $$ = std::make_unique<boot::identifier>($1, boot::make_position(@1)); } identifier_definition: - IDENTIFIER "*" { $$ = boot::identifier_definition{ $1, true }; } - | IDENTIFIER { $$ = boot::identifier_definition{ $1, false }; } + IDENTIFIER "*" { $$ = std::make_unique<boot::identifier_definition>($1, boot::make_position(@1), true); } + | IDENTIFIER { $$ = std::make_unique<boot::identifier_definition>($1, boot::make_position(@1), false); } identifier_definitions: identifier_definition "," identifier_definitions { $$ = $3; - $$.emplace($$.cbegin(), $1); + $$.emplace($$.cbegin(), std::move(*$1)); } - | identifier_definition { $$.emplace_back($1); } + | identifier_definition { $$.emplace_back(std::move(*$1)); } return_declaration: /* proper procedure */ {} | ":" "!" { $$ = boot::procedure_type_expression::return_t(std::monostate{}); } @@ -195,11 +199,11 @@ procedure_heading: "(" optional_fields ")" return_declaration procedure_declaration: "proc" identifier_definition procedure_heading procedure_body { - $$ = new boot::procedure_declaration(boot::make_position(@$), $2, $3, std::move(*$4)); + $$ = new boot::procedure_declaration(boot::make_position(@$), std::move(*$2), $3, std::move(*$4)); } | "proc" identifier_definition procedure_heading "extern" { - $$ = new boot::procedure_declaration(boot::make_position(@$), $2, $3); + $$ = new boot::procedure_declaration(boot::make_position(@$), std::move(*$2), $3); } procedure_part: /* no procedure declarations */ {} @@ -247,7 +251,8 @@ literal: traits_expression: TRAIT "(" type_expressions ")" { - $$ = new boot::traits_expression(boot::make_position(@$), $1, $3); + $$ = new boot::traits_expression(boot::make_position(@$), + boot::identifier($1, boot::make_position(@1)), $3); } simple_expression: literal { $$ = $1; } @@ -256,9 +261,9 @@ simple_expression: | cast_expression { $$ = $1; } | call_expression { $$ = $1; } | "(" expression ")" { $$ = $2; } - | IDENTIFIER "{" field_initializers "}" + | identifier "{" field_initializers "}" { - $$ = new boot::record_constructor_expression(boot::make_position(@$), $1, $3); + $$ = new boot::record_constructor_expression(boot::make_position(@$), std::move(*$1), $3); } | "[" INTEGER "]" type_expression "{" expressions "}" { @@ -365,8 +370,8 @@ type_expressions: designator_expression: simple_expression "[" expression "]" { $$ = new boot::array_access_expression(boot::make_position(@$), $1, $3); } - | simple_expression "." IDENTIFIER - { $$ = new boot::field_access_expression(boot::make_position(@$), $1, $3); } + | simple_expression "." identifier + { $$ = new boot::field_access_expression(boot::make_position(@$), $1, std::move(*$3)); } | simple_expression "^" { $$ = new boot::dereference_expression(boot::make_position(@$), $1); } | IDENTIFIER @@ -426,14 +431,14 @@ optional_fields: required_fields { $$ = $1; } | /* no fields */ {} field_initializer: - IDENTIFIER ":" expression { $$.name = $1; $$.value = $3; } + identifier ":" expression { $$ = std::make_unique<boot::field_initializer>(std::move(*$1), $3); } field_initializers: field_initializer "," field_initializers { $$ = $3; - $$.emplace($$.cbegin(), $1); + $$.emplace($$.cbegin(), std::move(*$1)); } - | field_initializer { $$.push_back($1); } + | field_initializer { $$.push_back(std::move(*$1)); } type_expression: "[" INTEGER "]" type_expression { @@ -447,9 +452,9 @@ type_expression: { $$ = new boot::record_type_expression(boot::make_position(@$), $2); } - | "record" "(" IDENTIFIER ")" optional_fields "end" + | "record" "(" identifier ")" optional_fields "end" { - $$ = new boot::record_type_expression(boot::make_position(@$), $5, $3); + $$ = new boot::record_type_expression(boot::make_position(@$), $5, std::move(*$3)); } | "proc" procedure_heading { @@ -464,12 +469,12 @@ type_expression: $$ = new boot::named_expression(boot::make_position(@$), $1); } identifiers: - IDENTIFIER "," identifiers + identifier "," identifiers { $$ = $3; - $$.emplace($$.cbegin(), $1); + $$.emplace($$.cbegin(), std::move(*$1)); } - | IDENTIFIER { $$.emplace_back($1); } + | identifier { $$.emplace_back(std::move(*$1)); } variable_declaration: identifier_definitions ":" type_expression { @@ -498,7 +503,7 @@ variable_part: | "var" variable_declarations { $$ = $2; } constant_declaration: identifier_definition ":=" expression { - $$ = new boot::constant_declaration(boot::make_position(@$), $1, $3); + $$ = new boot::constant_declaration(boot::make_position(@$), std::move(*$1), $3); } constant_declarations: constant_declaration constant_declarations @@ -532,7 +537,7 @@ import_part: | "import" import_declarations { $$ = $2; } type_declaration: identifier_definition "=" type_expression { - $$ = new boot::type_declaration(boot::make_position(@$), $1, $3); + $$ = new boot::type_declaration(boot::make_position(@$), std::move(*$1), $3); } type_declarations: type_declaration type_declarations diff --git a/boot/result.cc b/boot/result.cc index 90d0ad6..5f3c516 100644 --- a/boot/result.cc +++ b/boot/result.cc @@ -19,22 +19,57 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { - location::location(std::size_t line, std::size_t column) - : line(line), column(column) + location::location(const std::size_t line, const std::size_t column) + : m_line(line), m_column(column) { } + std::size_t location::line() const + { + return this->m_line; + } + + std::size_t location::column() const + { + return this->m_column; + } + bool location::available() const { - return this->line != 0 || this->column != 0; + return this->m_line != 0 || this->m_column != 0; + } + + bool location::operator==(const location& that) const + { + return this->m_line == that.m_line && this->m_column == that.m_column; + } + + bool location::operator!=(const location& that) const + { + return !(*this == that); } - position::position(location start, location end) - : start(start), end(end) + source_position::source_position(location start, location end) + : m_start(start), m_end(end) { } - error::error(const struct position position) + const location& source_position::start() const + { + return this->m_start; + } + + const location& source_position::end() const + { + return this->m_end; + } + + bool source_position::is_span() const + { + return this->m_start != this->m_end; + } + + error::error(const source_position position) : position(position) { } @@ -49,19 +84,65 @@ namespace elna::boot return !m_errors.empty(); } - bool identifier_definition::operator==(const identifier_definition& that) const + identifier::identifier(const std::string& name, const source_position& position) + : m_name(name), m_position(position) + { + } + + const std::string& identifier::name() const + { + return this->m_name; + } + + const source_position& identifier::position() const + { + return this->m_position; + } + + bool identifier::operator==(const identifier& that) const + { + return this->m_name == that.m_name; + } + + bool identifier::operator!=(const identifier& that) const + { + return !(*this == that); + } + + bool identifier::operator==(const std::string& that) const + { + return this->m_name == that; + } + + bool identifier::operator!=(const std::string& that) const + { + return !(*this == that); + } + + identifier_definition::identifier_definition(const std::string& name, + const source_position& position, const bool exported) + : m_identifier(name, position), m_exported(exported) + { + } + + const std::string& identifier_definition::name() const + { + return this->m_identifier.name(); + } + + const identifier& identifier_definition::id() const { - return *this == that.name; + return this->m_identifier; } - bool identifier_definition::operator==(const std::string& that) const + bool identifier_definition::exported() const { - return this->name == that; + return this->m_exported; } } -std::size_t std::hash<elna::boot::identifier_definition>::operator()( - const elna::boot::identifier_definition& key) const +std::size_t std::hash<elna::boot::identifier>::operator()( + const elna::boot::identifier& key) const { - return std::hash<std::string>{}(key.name); + return std::hash<std::string>{}(key.name()); } diff --git a/boot/semantic.cc b/boot/semantic.cc index dd9022b..031adfc 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -22,8 +22,8 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { declaration_error::declaration_error(const kind error_kind, - const std::string& identifier, const struct position position) - : error(position), identifier(identifier), error_kind(error_kind) + const boot::identifier& identifier) + : error(identifier.position()), identifier(identifier.name()), error_kind(error_kind) { } @@ -33,8 +33,6 @@ namespace elna::boot { case kind::undeclared: return "Type '" + identifier + "' not declared"; - case kind::already: - return "Symbol '" + identifier + "' has been already declared"; case kind::local_export: return "Local symbol '" + this->identifier + "' cannot be exported"; default: @@ -42,38 +40,107 @@ namespace elna::boot } } - type_expectation_error::type_expectation_error(const kind error_kind, const struct position position) - : error(position), error_kind(error_kind) + redefinition_error::redefinition_error(const boot::identifier& identifier, + std::optional<source_position> original) + : error(identifier.position()), identifier(identifier.name()), original(original) { } - std::string type_expectation_error::what() const + std::string redefinition_error::what() const { - switch (this->error_kind) + return "Symbol '" + identifier + "' has been already defined"; + } + + std::optional<std::pair<std::string, source_position>> + redefinition_error::note() const + { + if (original.has_value() && original->start().available()) { - case kind::assignment: - return "Assinging an incompatible type"; - case kind::result: - return "Procedure return type and type of the return expression are not compatible"; - case kind::argument: - return "Argument type does not match parameter"; - default: - __builtin_unreachable(); + return std::make_pair("previously declared here", *original); + } + return std::nullopt; + } + + type_mismatch_error::type_mismatch_error(const source_position position, + type expected, type actual) + : error(position), expected(expected), actual(actual) + { + } + + std::string type_mismatch_error::what() const + { + return "Expected type '" + expected.to_string() + + "', but got '" + actual.to_string() + "'"; + } + + field_not_found_error::field_not_found_error(const identifier& field_name, + type composite_type) + : error(field_name.position()), field_name(field_name.name()), composite_type(composite_type) + { + } + + std::string field_not_found_error::what() const + { + type resolved = inner_aliased_type(composite_type); + bool is_enum = resolved.get<enumeration_type>() != nullptr; + bool is_record = resolved.get<record_type>() != nullptr; + + if (is_enum || is_record) + { + std::string message = is_enum ? "Enumeration" : "Record"; + + if (auto alias = composite_type.get<alias_type>()) + { + message += " '" + alias->name + "'"; + } + message += " does not have a "; + message += is_enum ? "member" : "field"; + message += " named '" + field_name + "'"; + return message; } + return "Type '" + composite_type.to_string() + + "' does not have a field named '" + field_name + "'"; } - field_duplication_error::field_duplication_error(const std::string& field_name, const struct position position) - : error(position), field_name(field_name) + duplicate_member_error::duplicate_member_error(const boot::identifier& member_name, + type aggregate, std::optional<source_position> original, + std::optional<std::string> base_name) + : error(member_name.position()), member_name(member_name.name()), aggregate(aggregate), + original(original), base_name(base_name) { } - std::string field_duplication_error::what() const + std::string duplicate_member_error::what() const { - return "Repeated field name '" + field_name + "'"; + type resolved = inner_aliased_type(aggregate); + bool is_enum = resolved.get<enumeration_type>() != nullptr; + std::string kind = is_enum ? "member" : "field"; + std::string message = is_enum ? "Enumeration" : "Record"; + + if (auto alias = aggregate.get<alias_type>()) + { + message += " '" + alias->name + "'"; + } + message += " already has a " + kind + " named '" + member_name + "'"; + + if (base_name.has_value()) + { + message += " (defined in base type '" + *base_name + "')"; + } + return message; + } + + std::optional<std::pair<std::string, source_position>> duplicate_member_error::note() const + { + if (original.has_value() && original->start().available()) + { + return std::make_pair("previously declared here", *original); + } + return std::nullopt; } cyclic_declaration_error::cyclic_declaration_error(const std::vector<std::string>& cycle, - const struct position position) + const source_position position) : error(position), cycle(cycle) { } @@ -91,28 +158,36 @@ namespace elna::boot return message; } - return_error::return_error(const std::string& identifier, const struct position position) - : error(position), identifier(identifier) + return_error::return_error(const std::string& identifier, const source_position position, + type return_type) + : error(position), identifier(identifier), return_type(return_type) { } std::string return_error::what() const { - return "Procedure '" + this->identifier + "' is expected to return, but does not have a return statement"; + if (!return_type.empty()) + { + return "Procedure '" + this->identifier + + "' does not return a value, but return expression has type '" + + return_type.to_string() + "'"; + } + return "Procedure '" + this->identifier + + "' is expected to return, but does not have a return statement"; } - base_type_error::base_type_error(const std::string& base, const struct position position) - : error(position), base(base) + base_type_error::base_type_error(type actual, const source_position position) + : error(position), actual(actual) { } std::string base_type_error::what() const { - return "Record base type '" + this->base + "' is not a record."; + return "'" + actual.to_string() + "' is not a record type"; } argument_count_error::argument_count_error(std::size_t expected, std::size_t actual, - const struct position position) + const source_position position) : error(position), expected(expected), actual(actual) { } @@ -138,7 +213,7 @@ namespace elna::boot void type_analysis_visitor::visit(procedure_declaration *declaration) { - this->current_procedure = this->bag.lookup(declaration->identifier.name)->is_procedure(); + this->current_procedure = this->bag.lookup(declaration->identifier.name())->is_procedure(); if (declaration->body.has_value()) { @@ -157,17 +232,19 @@ namespace elna::boot { if (!is_assignable_from(return_type, return_expr->type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::result, return_expr->position()); + add_error<type_mismatch_error>( + return_expr->position(), return_type, return_expr->type_decoration); } } else { - add_error<type_expectation_error>(type_expectation_error::kind::result, return_expr->position()); + add_error<return_error>(declaration->identifier.name(), + return_expr->position(), return_expr->type_decoration); } } else if (declaration->heading().return_type.proper_type != nullptr) { - add_error<return_error>(declaration->identifier.name, declaration->position()); + add_error<return_error>(declaration->identifier.name(), declaration->position()); } this->bag.leave(); } @@ -185,7 +262,8 @@ namespace elna::boot if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::assignment, statement->position()); + add_error<type_mismatch_error>(statement->position(), + statement->lvalue().type_decoration, statement->rvalue().type_decoration); } } @@ -199,11 +277,12 @@ namespace elna::boot } for (const identifier_definition& variable_identifier : declaration->identifiers) { - auto variable_symbol = this->bag.lookup(variable_identifier.name)->is_variable(); + auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable(); if (!is_assignable_from(variable_symbol->symbol, declaration->initializer->type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::assignment, - declaration->initializer->position()); + add_error<type_mismatch_error>( + declaration->initializer->position(), + variable_symbol->symbol, declaration->initializer->type_decoration); } } } @@ -219,8 +298,8 @@ namespace elna::boot { if (!is_assignable_from(condition_type, case_label->type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::assignment, - case_label->position()); + add_error<type_mismatch_error>( + case_label->position(), condition_type, case_label->type_decoration); } } } @@ -245,7 +324,7 @@ namespace elna::boot void type_analysis_visitor::visit(type_declaration *declaration) { std::vector<std::string> alias_path; - auto unresolved_type = this->bag.lookup(declaration->identifier.name)->is_type()->symbol.get<alias_type>(); + auto unresolved_type = this->bag.lookup(declaration->identifier.name())->is_type()->symbol.get<alias_type>(); if (!check_unresolved_symbol(unresolved_type, alias_path)) { @@ -261,10 +340,10 @@ namespace elna::boot { if (expression->base.has_value()) { - type base_type = inner_aliased_type(this->bag.lookup(expression->base.value())->is_type()->symbol); + type base_type = inner_aliased_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol); if (base_type.get<record_type>() == nullptr) { - add_error<base_type_error>(expression->base.value(), expression->position()); + add_error<base_type_error>(base_type, expression->position()); } } walking_visitor::visit(expression); @@ -285,8 +364,9 @@ namespace elna::boot (*argument_iterator)->accept(this); if (!is_assignable_from(*type_iterator, (*argument_iterator)->type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::argument, - (*argument_iterator)->position()); + add_error<type_mismatch_error>( + (*argument_iterator)->position(), *type_iterator, + (*argument_iterator)->type_decoration); } ++argument_iterator; ++type_iterator; @@ -305,19 +385,22 @@ namespace elna::boot if (record == nullptr) { - add_error<type_expectation_error>(type_expectation_error::kind::result, expression->position()); + add_error<type_mismatch_error>( + expression->position(), type(std::make_shared<record_type>()), + expression->type_decoration); return; } for (const field_initializer& initializer : expression->field_initializers) { for (const type_field& field : record->fields) { - if (field.first == initializer.name) + if (field.first == initializer.name()) { - if (!is_assignable_from(field.second, initializer.value->type_decoration)) + if (!is_assignable_from(field.second, initializer.value().type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::argument, - initializer.value->position()); + add_error<type_mismatch_error>( + initializer.value().position(), field.second, + initializer.value().type_decoration); } break; } @@ -331,7 +414,9 @@ namespace elna::boot if (array == nullptr) { - add_error<type_expectation_error>(type_expectation_error::kind::result, expression->position()); + add_error<type_mismatch_error>( + expression->position(), type(std::make_shared<array_type>(type(), 0)), + expression->type_decoration); return; } if (expression->elements.size() > array->size) @@ -344,8 +429,8 @@ namespace elna::boot { if (!is_assignable_from(array->base, element->type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::argument, - element->position()); + add_error<type_mismatch_error>( + element->position(), array->base, element->type_decoration); } } } @@ -382,7 +467,7 @@ namespace elna::boot for (auto& parameter_name : parameter_names) { result_type.first.parameters.push_back(this->current_type); - result_type.second.push_back(parameter_name); + result_type.second.push_back(parameter_name.name()); } } return result_type; @@ -468,11 +553,12 @@ namespace elna::boot void name_analysis_visitor::visit(type_declaration *declaration) { walking_visitor::visit(declaration); - auto resolved = this->bag.resolve(declaration->identifier.name, this->current_type); + auto resolved = this->bag.resolve(declaration->identifier.name(), this->current_type); auto info = std::make_shared<type_info>(type(resolved)); - info->exported = declaration->identifier.exported; - this->bag.enter(declaration->identifier.name, info); + info->exported = declaration->identifier.exported(); + info->position.emplace(declaration->position()); + this->bag.enter(declaration->identifier.name(), info); } void name_analysis_visitor::visit(pointer_type_expression *expression) @@ -492,7 +578,8 @@ namespace elna::boot /** * Collects field names from a record type recursively, base first. */ - static void collect_field_names(const type& composite_type, std::set<std::string>& names) + static void collect_field_names(const type& composite_type, + std::map<std::string, field_origin>& names) { auto record = inner_aliased_type(composite_type).get<record_type>(); if (record == nullptr) @@ -505,12 +592,14 @@ namespace elna::boot } for (auto& field : record->fields) { - names.insert(field.first); + names.insert({ field.first, field_origin{ std::nullopt, composite_type } }); } } std::vector<type_field> name_analysis_visitor::build_composite_type( - const std::vector<field_declaration>& fields, std::set<std::string>& field_names) + const std::vector<field_declaration>& fields, + std::map<std::string, field_origin>& field_names, + type aggregate) { std::vector<type_field> result; @@ -519,13 +608,27 @@ namespace elna::boot field.second->accept(this); for (auto& field_name : field.first) { - if (!field_names.insert(field_name).second) + auto existing = field_names.find(field_name.name()); + if (existing != field_names.end()) { - add_error<field_duplication_error>(field_name, field.second->position()); + std::optional<std::string> base_name; + + if (!existing->second.declaration.has_value() + && !existing->second.base_type.empty()) + { + if (auto alias = existing->second.base_type.get<alias_type>()) + { + base_name = alias->name; + } + } + add_error<duplicate_member_error>(field_name, aggregate, + existing->second.declaration, base_name); } else { - result.push_back(std::make_pair(field_name, this->current_type)); + field_names.insert({ field_name.name(), + field_origin{ field.second->position(), type() } }); + result.push_back(std::make_pair(field_name.name(), this->current_type)); } } } @@ -537,11 +640,11 @@ namespace elna::boot std::shared_ptr<record_type> result_type; if (expression->base.has_value()) { - if (auto unresolved_alias = this->bag.declared(expression->base.value())) + if (auto unresolved_alias = this->bag.declared(expression->base.value().name())) { result_type = std::make_shared<record_type>(type(unresolved_alias)); } - else if (auto base_symbol = this->bag.lookup(expression->base.value())) + else if (auto base_symbol = this->bag.lookup(expression->base.value().name())) { if (auto base_type_info = base_symbol->is_type()) { @@ -549,7 +652,17 @@ namespace elna::boot } else { - add_error<base_type_error>(expression->base.value(), expression->position()); + type actual; + + if (auto var = base_symbol->is_variable()) + { + actual = var->symbol; + } + else if (auto proc = base_symbol->is_procedure()) + { + actual = type(std::make_shared<procedure_type>(proc->symbol)); + } + add_error<base_type_error>(actual, expression->position()); this->current_type = type(); return; } @@ -557,7 +670,7 @@ namespace elna::boot else { add_error<declaration_error>(declaration_error::kind::undeclared, - expression->base.value(), expression->position()); + expression->base.value()); this->current_type = type(); return; } @@ -567,30 +680,35 @@ namespace elna::boot result_type = std::make_shared<record_type>(); } - std::set<std::string> field_names; + std::map<std::string, field_origin> field_names; collect_field_names(result_type->base, field_names); - result_type->fields = build_composite_type(expression->fields, field_names); + result_type->fields = build_composite_type(expression->fields, field_names, type(result_type)); this->current_type = type(result_type); } void name_analysis_visitor::visit(record_constructor_expression *expression) { - if (auto type_symbol = this->bag.lookup(expression->type_name)) + if (auto type_symbol = this->bag.lookup(expression->type_name.name())) { if (auto type_info = type_symbol->is_type()) { expression->type_decoration = type_info->symbol; } } + else + { + add_error<declaration_error>(declaration_error::kind::undeclared, + expression->type_name); + } for (const field_initializer& initializer : expression->field_initializers) { - initializer.value->accept(this); + initializer.value().accept(this); if (!expression->type_decoration.empty() - && lookup_field(expression->type_decoration, initializer.name).empty()) + && lookup_field(expression->type_decoration, initializer.name()).empty()) { add_error<declaration_error>(declaration_error::kind::undeclared, - initializer.name, expression->position()); + initializer.id()); } } } @@ -616,19 +734,42 @@ namespace elna::boot void name_analysis_visitor::visit(enumeration_type_expression *expression) { - std::shared_ptr<enumeration_type> result_type = std::make_shared<enumeration_type>(expression->members); + std::vector<std::string> member_names; + for (auto& member : expression->members) + { + member_names.emplace_back(member.name()); + } + std::shared_ptr<enumeration_type> result_type = std::make_shared<enumeration_type>( + member_names); + std::map<std::string, source_position> seen; + type aggregate(result_type); + for (auto& member : expression->members) + { + auto existing = seen.find(member.name()); + if (existing != seen.end()) + { + add_error<duplicate_member_error>(member, aggregate, existing->second); + } + else + { + seen.insert({ member.name(), member.position() }); + } + } this->current_type = type(result_type); } std::shared_ptr<variable_info> name_analysis_visitor::register_variable(const std::string& name, - const bool is_extern, const struct position position) + const bool is_extern, const source_position position) { auto variable_symbol = std::make_shared<variable_info>(this->current_type, is_extern); + variable_symbol->position.emplace(position); if (!this->bag.enter(name, variable_symbol)) { - add_error<declaration_error>(declaration_error::kind::already, name, position); + auto original = this->bag.lookup(name); + add_error<redefinition_error>(boot::identifier(name, position), + original->position); } return variable_symbol; } @@ -644,9 +785,9 @@ namespace elna::boot } for (const identifier_definition& variable_identifier : declaration->identifiers) { - auto variable_symbol = register_variable(variable_identifier.name, declaration->is_extern, + auto variable_symbol = register_variable(variable_identifier.name(), declaration->is_extern, declaration->position()); - variable_symbol->exported = variable_identifier.exported; + variable_symbol->exported = variable_identifier.exported(); } } @@ -655,8 +796,9 @@ namespace elna::boot walking_visitor::visit(declaration); auto constant_symbol = std::make_shared<constant_info>(this->current_literal); - constant_symbol->exported = declaration->identifier.exported; - this->bag.enter(declaration->identifier.name, constant_symbol); + constant_symbol->exported = declaration->identifier.exported(); + constant_symbol->position.emplace(declaration->position()); + this->bag.enter(declaration->identifier.name(), constant_symbol); } void name_analysis_visitor::visit(procedure_declaration *declaration) @@ -701,8 +843,9 @@ namespace elna::boot { info = std::make_shared<procedure_info>(heading, std::move(parameter_names)); } - info->exported = declaration->identifier.exported; - this->bag.enter(declaration->identifier.name, info); + info->exported = declaration->identifier.exported(); + info->position.emplace(declaration->position()); + this->bag.enter(declaration->identifier.name(), info); } void name_analysis_visitor::visit(procedure_call *call) @@ -775,6 +918,11 @@ namespace elna::boot { trait->type_decoration = trait->types.empty() ? type() : trait->types.front(); } + else + { + add_error<declaration_error>(declaration_error::kind::undeclared, + trait->name); + } } void name_analysis_visitor::visit(binary_expression *expression) @@ -841,13 +989,18 @@ namespace elna::boot void name_analysis_visitor::visit(field_access_expression *expression) { walking_visitor::visit(expression); - expression->type_decoration = lookup_field(expression->base().type_decoration, expression->field()); + expression->type_decoration = lookup_field(expression->base().type_decoration, expression->field().name()); auto is_designator = expression->base().is_designator(); if (expression->type_decoration.empty() && is_designator != nullptr && is_designator->is_named() != nullptr) { expression->type_decoration = this->current_type; } + if (expression->type_decoration.empty()) + { + add_error<field_not_found_error>(expression->field(), + expression->base().type_decoration); + } } void name_analysis_visitor::visit(dereference_expression *expression) @@ -896,7 +1049,7 @@ namespace elna::boot else { add_error<declaration_error>(declaration_error::kind::undeclared, - expression->name, expression->position()); + boot::identifier(expression->name, expression->position())); } } @@ -969,12 +1122,12 @@ namespace elna::boot void declaration_visitor::visit(type_declaration *declaration) { - const std::string& type_identifier = declaration->identifier.name; + const std::string& type_identifier = declaration->identifier.name(); if (!this->unresolved.insert({ type_identifier, std::make_shared<alias_type>(type_identifier) }).second) { - add_error<declaration_error>(declaration_error::kind::already, - declaration->identifier.name, declaration->position()); + add_error<redefinition_error>(declaration->identifier.id(), + declaration->position()); } } @@ -998,20 +1151,20 @@ namespace elna::boot { for (const identifier_definition& variable_identifier : declaration->identifiers) { - if (variable_identifier.exported) + if (variable_identifier.exported()) { add_error<declaration_error>(declaration_error::kind::local_export, - variable_identifier.name, declaration->position()); + variable_identifier.id()); } } } void declaration_visitor::visit(constant_declaration *declaration) { - if (declaration->identifier.exported) + if (declaration->identifier.exported()) { add_error<declaration_error>(declaration_error::kind::local_export, - declaration->identifier.name, declaration->position()); + declaration->identifier.id()); } } diff --git a/boot/symbol.cc b/boot/symbol.cc index d7ceacb..a3a36c1 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -270,6 +270,54 @@ namespace elna::boot return tag == type_tag::empty; } + std::string type::to_string() const + { + switch (tag) + { + case type_tag::empty: + return "<empty>"; + case type_tag::alias: + return alias.lock()->name; + case type_tag::primitive: + return primitive->identifier; + case type_tag::pointer: + return "^" + pointer->base.to_string(); + case type_tag::array: + return "[" + std::to_string(array->size) + "]" + array->base.to_string(); + case type_tag::record: + if (record->base.empty()) + { + return "record ... end"; + } + else + { + return "record(" + record->base.to_string() + ") ... end"; + } + case type_tag::procedure: + { + std::string result = "proc("; + for (std::size_t i = 0; i < procedure->parameters.size(); ++i) + { + if (i > 0) result += ", "; + result += procedure->parameters[i].to_string(); + } + result += ")"; + if (procedure->return_type.no_return) + { + result += ": !"; + } + else if (!procedure->return_type.proper_type.empty()) + { + result += ": " + procedure->return_type.proper_type.to_string(); + } + return result; + } + case type_tag::enumeration: + return "(enumeration)"; + } + __builtin_unreachable(); + } + alias_type::alias_type(const std::string& name) : name(name), reference() { diff --git a/gcc/gcc/elna-diagnostic.cc b/gcc/gcc/elna-diagnostic.cc index f7b660d..3769ee4 100644 --- a/gcc/gcc/elna-diagnostic.cc +++ b/gcc/gcc/elna-diagnostic.cc @@ -38,27 +38,31 @@ namespace elna::gcc linemap_add(line_table, LC_LEAVE, 0, NULL, 0); } - location_t get_location(const boot::position *position) + location_t get_location(const boot::source_position *position) { - linemap_line_start(line_table, position->start.line, 0); + linemap_line_start(line_table, position->start().line(), 0); - return linemap_position_for_column(line_table, position->start.column); + return linemap_position_for_column(line_table, position->start().column()); } - void fill_range(const boot::position& position, location_t& start, location_t& end) + location_t make_range(const boot::source_position& position) { - linemap_line_start(line_table, position.start.line, 0); - start = linemap_position_for_column(line_table, position.start.column); + linemap_line_start(line_table, position.start().line(), 0); + location_t caret = linemap_position_for_column(line_table, position.start().column()); + location_t start = caret; + location_t end; - if (position.start.line != position.end.line || position.start.column != position.end.column) + if (position.is_span()) { - linemap_line_start(line_table, position.end.line, 0); - end = linemap_position_for_column(line_table, position.end.column); + linemap_line_start(line_table, position.end().line(), 0); + end = linemap_position_for_column(line_table, position.end().column()); } else { end = start; } + + return make_location(caret, start, end); } std::string print_aggregate_name(tree type, const std::string& kind_name) @@ -174,18 +178,20 @@ namespace elna::gcc { for (const auto& error : errors) { - if (error->position.start.available()) + if (error->position.start().available()) { - location_t start, end; - fill_range(error->position, start, end); - rich_location rich_loc(line_table, start); - rich_loc.add_range(end); - error_at(&rich_loc, error->what().c_str()); + location_t loc = make_range(error->position); + error_at(loc, "%s", error->what().c_str()); } else { location_t gcc_location{ UNKNOWN_LOCATION }; - error_at(gcc_location, error->what().c_str()); + error_at(gcc_location, "%s", error->what().c_str()); + } + if (auto note = error->note()) + { + location_t note_loc = make_range(note->second); + inform(note_loc, "%s", note->first.c_str()); } } } diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index 18d31d6..aace38b 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -180,12 +180,10 @@ namespace elna::gcc void generic_visitor::visit(boot::record_constructor_expression *expression) { - tree type_decl = this->symbols->lookup(expression->type_name); + tree type_decl = this->symbols->lookup(expression->type_name.name()); if (type_decl == NULL_TREE) { - error_at(get_location(&expression->position()), "Type '%s' not declared", - expression->type_name.c_str()); this->current_expression = error_mark_node; return; } @@ -195,8 +193,8 @@ namespace elna::gcc for (const boot::field_initializer& initializer : expression->field_initializers) { tree field_decl = find_field_by_name(get_location(&expression->position()), - record_type, initializer.name); - initializer.value->accept(this); + record_type, initializer.name()); + initializer.value().accept(this); CONSTRUCTOR_APPEND_ELT(tree_arguments, field_decl, this->current_expression); } this->current_expression = build_constructor(record_type, tree_arguments); @@ -294,7 +292,7 @@ namespace elna::gcc void generic_visitor::visit(boot::procedure_declaration *declaration) { - tree fndecl = this->symbols->lookup(declaration->identifier.name); + tree fndecl = this->symbols->lookup(declaration->identifier.name()); if (!declaration->body.has_value()) { @@ -304,7 +302,7 @@ namespace elna::gcc DECL_STRUCT_FUNCTION(fndecl)->language = ggc_cleared_alloc<language_function>(); enter_scope(); - this->bag.enter(this->bag.lookup(declaration->identifier.name)->is_procedure()->scope); + this->bag.enter(this->bag.lookup(declaration->identifier.name())->is_procedure()->scope); tree argument_chain = DECL_ARGUMENTS(fndecl); for (; argument_chain != NULL_TREE; argument_chain = TREE_CHAIN(argument_chain)) @@ -712,15 +710,15 @@ namespace elna::gcc return; } tree definition_tree = build_decl(definition_location, CONST_DECL, - get_identifier(declaration->identifier.name.c_str()), TREE_TYPE(this->current_expression)); - auto result = this->symbols->enter(declaration->identifier.name, definition_tree); + get_identifier(declaration->identifier.name().c_str()), TREE_TYPE(this->current_expression)); + auto result = this->symbols->enter(declaration->identifier.name(), definition_tree); if (result) { DECL_INITIAL(definition_tree) = this->current_expression; TREE_CONSTANT(definition_tree) = 1; TREE_READONLY(definition_tree) = 1; - TREE_PUBLIC(definition_tree) = declaration->identifier.exported; + TREE_PUBLIC(definition_tree) = declaration->identifier.exported(); if (!lang_hooks.decls.global_bindings_p()) { @@ -729,11 +727,6 @@ namespace elna::gcc append_statement(declaration_statement); } } - else - { - error_at(definition_location, "Variable '%s' already declared in this scope", - declaration->identifier.name.c_str()); - } this->current_expression = NULL_TREE; } @@ -742,13 +735,13 @@ namespace elna::gcc for (const auto& variable_identifier : declaration->identifiers) { location_t declaration_location = get_location(&declaration->position()); - tree declaration_tree = this->symbols->lookup(variable_identifier.name); + tree declaration_tree = this->symbols->lookup(variable_identifier.name()); if (declaration_tree == NULL_TREE) { - auto variable_symbol = this->bag.lookup(variable_identifier.name)->is_variable(); + auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable(); - declaration_tree = declare_variable(variable_identifier.name, *variable_symbol, this->symbols); + declaration_tree = declare_variable(variable_identifier.name(), *variable_symbol, this->symbols); } // Set initializer if given. if (declaration->initializer != nullptr) @@ -764,7 +757,7 @@ namespace elna::gcc if (lang_hooks.decls.global_bindings_p()) { - TREE_STATIC(declaration_tree) = !variable_identifier.exported && !declaration->is_extern; + TREE_STATIC(declaration_tree) = !variable_identifier.exported() && !declaration->is_extern; varpool_node::get_create(declaration_tree); varpool_node::finalize_decl(declaration_tree); } @@ -786,8 +779,6 @@ namespace elna::gcc if (symbol == NULL_TREE) { - error_at(get_location(&expression->position()), "Symbol '%s' not declared in the current scope", - expression->name.c_str()); this->current_expression = error_mark_node; } else @@ -843,7 +834,7 @@ namespace elna::gcc if (trait->arguments.size() != 1) { error_at(get_location(&trait->position()), "Trait '%s' expects 1 argument, got %lu", - trait->name.c_str(), trait->arguments.size()); + trait->name.name().c_str(), trait->arguments.size()); this->current_expression = error_mark_node; return false; } @@ -861,7 +852,7 @@ namespace elna::gcc else if (!is_integral_type(this->current_expression) && TREE_CODE(this->current_expression) != ENUMERAL_TYPE) { error_at(get_location(&trait->position()), "Type '%s' does not support trait '%s'", - print_type(this->current_expression).c_str(), trait->name.c_str()); + print_type(this->current_expression).c_str(), trait->name.name().c_str()); this->current_expression = error_mark_node; return false; } @@ -907,7 +898,7 @@ namespace elna::gcc if (trait->arguments.size() != 2) { error_at(trait_location, "Trait '%s' expects 2 arguments, got %lu", - trait->name.c_str(), trait->arguments.size()); + trait->name.name().c_str(), trait->arguments.size()); this->current_expression = error_mark_node; return; } @@ -936,7 +927,6 @@ namespace elna::gcc } else { - error_at(get_location(&trait->position()), "Trait '%s' is unknown", trait->name.c_str()); this->current_expression = error_mark_node; } } @@ -964,19 +954,18 @@ namespace elna::gcc for (iterator = TYPE_VALUES(aggregate_type); iterator != NULL_TREE; iterator = TREE_CHAIN(iterator)) { - if (IDENTIFIER_POINTER(TREE_PURPOSE(iterator)) == expression->field()) + if (IDENTIFIER_POINTER(TREE_PURPOSE(iterator)) == expression->field().name()) { this->current_expression = TREE_VALUE(iterator); return; } } this->current_expression = error_mark_node; - error_at(expression_location, "Unknown enumeration member '%s'", expression->field().c_str()); } else { tree field_declaration = find_field_by_name(expression_location, - TREE_TYPE(this->current_expression), expression->field()); + TREE_TYPE(this->current_expression), expression->field().name()); if (field_declaration != error_mark_node) { diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 6a5f32d..b6aad70 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -62,7 +62,6 @@ namespace elna::boot class cast_expression; class record_constructor_expression; class array_constructor_expression; - struct field_initializer; class assign_statement; class if_statement; class import_declaration; @@ -224,13 +223,13 @@ namespace elna::boot */ class node { - const struct position source_position; + const source_position m_position; protected: /** * \param position Source code position. */ - explicit node(const position position); + explicit node(const source_position position); public: virtual void accept(parser_visitor *visitor) = 0; @@ -239,7 +238,7 @@ namespace elna::boot /** * \return Node position in the source code. */ - const struct position& position() const; + const source_position& position() const; }; class statement : public virtual node @@ -266,7 +265,7 @@ namespace elna::boot class declaration : public node { protected: - declaration(const struct position position, identifier_definition identifier); + declaration(const source_position position, identifier_definition identifier); public: const identifier_definition identifier; @@ -293,7 +292,7 @@ namespace elna::boot public: const std::uint32_t size; - array_type_expression(const struct position position, + array_type_expression(const source_position position, type_expression *base, const std::uint32_t size); ~array_type_expression() override; @@ -308,7 +307,7 @@ namespace elna::boot type_expression *m_base; public: - pointer_type_expression(const struct position position, type_expression *base); + pointer_type_expression(const source_position position, type_expression *base); ~pointer_type_expression() override; void accept(parser_visitor *visitor) override; @@ -317,18 +316,18 @@ namespace elna::boot type_expression& base(); }; - using field_declaration = std::pair<std::vector<std::string>, std::shared_ptr<type_expression>>; + using field_declaration = std::pair<std::vector<identifier>, std::shared_ptr<type_expression>>; class record_type_expression : public type_expression { public: const std::vector<field_declaration> fields; - const std::optional<std::string> base; + const std::optional<identifier> base; - record_type_expression(const struct position position, + record_type_expression(const source_position position, std::vector<field_declaration>&& fields); - record_type_expression(const struct position position, - std::vector<field_declaration>&& fields, std::string&& base); + record_type_expression(const source_position position, + std::vector<field_declaration>&& fields, identifier&& base); void accept(parser_visitor *visitor) override; record_type_expression *is_record() override; @@ -336,22 +335,34 @@ namespace elna::boot struct field_initializer { - std::string name; - expression *value; + field_initializer(identifier name, expression *value); + field_initializer(const field_initializer&) = delete; + field_initializer(field_initializer&& other) noexcept; + + field_initializer& operator=(const field_initializer&) = delete; + field_initializer& operator=(field_initializer&& other) noexcept; + + ~field_initializer(); + + const std::string& name() const; + const identifier& id() const; + expression& value() const; + + private: + identifier m_name; + expression *m_value; }; class record_constructor_expression : public expression { public: - const std::string type_name; + const identifier type_name; const std::vector<field_initializer> field_initializers; - record_constructor_expression(const struct position position, - std::string&& type_name, + record_constructor_expression(const source_position position, + identifier&& type_name, std::vector<field_initializer>&& field_initializers); void accept(parser_visitor *visitor) override; - - virtual ~record_constructor_expression() override; }; class array_constructor_expression : public expression @@ -361,7 +372,7 @@ namespace elna::boot type_expression *const m_element_type; const std::vector<expression *> elements; - array_constructor_expression(const struct position position, + array_constructor_expression(const source_position position, std::uint32_t size, type_expression *element_type, std::vector<expression *>&& elements); void accept(parser_visitor *visitor) override; @@ -375,10 +386,10 @@ namespace elna::boot class enumeration_type_expression : public type_expression { public: - const std::vector<std::string> members; + const std::vector<identifier> members; - enumeration_type_expression(const struct position, - std::vector<std::string>&& members); + enumeration_type_expression(const source_position, + std::vector<identifier>&& members); void accept(parser_visitor *visitor) override; enumeration_type_expression *is_enumeration() override; @@ -392,10 +403,10 @@ namespace elna::boot std::shared_ptr<type_expression> m_variable_type; public: - variable_declaration(const struct position position, + variable_declaration(const source_position position, std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type, expression *initializer = nullptr); - variable_declaration(const struct position position, + variable_declaration(const source_position position, std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type, std::monostate); @@ -426,7 +437,7 @@ namespace elna::boot expression *m_initializer; public: - constant_declaration(const struct position position, identifier_definition identifier, + constant_declaration(const source_position position, identifier_definition identifier, expression *initializer); void accept(parser_visitor *visitor) override; @@ -446,7 +457,7 @@ namespace elna::boot const return_t return_type; const std::vector<field_declaration> parameters; - procedure_type_expression(const struct position position, + procedure_type_expression(const source_position position, std::vector<field_declaration>&& parameters, return_t return_type = return_t()); ~procedure_type_expression(); @@ -483,9 +494,9 @@ namespace elna::boot public: std::optional<procedure_body> body; - procedure_declaration(const struct position position, identifier_definition identifier, + procedure_declaration(const source_position position, identifier_definition identifier, procedure_type_expression *heading, procedure_body&& body); - procedure_declaration(const struct position position, identifier_definition identifier, + procedure_declaration(const source_position position, identifier_definition identifier, procedure_type_expression *heading); void accept(parser_visitor *visitor) override; @@ -502,7 +513,7 @@ namespace elna::boot type_expression *m_underlying_type; public: - type_declaration(const struct position position, identifier_definition identifier, + type_declaration(const source_position position, identifier_definition identifier, type_expression *expression); ~type_declaration() override; @@ -520,7 +531,7 @@ namespace elna::boot expression *m_value; public: - cast_expression(const struct position position, type_expression *target, expression *value); + cast_expression(const source_position position, type_expression *target, expression *value); void accept(parser_visitor *visitor) override; cast_expression *is_cast() override; @@ -534,10 +545,10 @@ namespace elna::boot { public: const std::vector<type_expression *> arguments; - const std::string name; + const identifier name; std::vector<type> types; - traits_expression(const struct position position, const std::string& name, + traits_expression(const source_position position, identifier&& name, std::vector<type_expression *>&& arguments); ~traits_expression(); @@ -576,7 +587,7 @@ namespace elna::boot const std::vector<switch_case> cases; const std::vector<statement *> *alternative; - case_statement(const struct position position, expression *condition, + case_statement(const source_position position, expression *condition, std::vector<switch_case>&& cases, std::vector<statement *> *alternative = nullptr); void accept(parser_visitor *visitor) override; expression& condition(); @@ -603,7 +614,7 @@ namespace elna::boot public: const std::string name; - named_expression(const struct position position, const std::string& name); + named_expression(const source_position position, const std::string& name); void accept(parser_visitor *visitor) override; named_expression *is_named() override; @@ -615,7 +626,7 @@ namespace elna::boot expression *m_index; public: - array_access_expression(const struct position position, expression *base, expression *index); + array_access_expression(const source_position position, expression *base, expression *index); void accept(parser_visitor *visitor) override; expression& base(); @@ -629,15 +640,15 @@ namespace elna::boot class field_access_expression : public designator_expression { expression *m_base; - std::string m_field; + identifier m_field; public: - field_access_expression(const struct position position, expression *base, - const std::string& field); + field_access_expression(const source_position position, expression *base, + identifier&& field); void accept(parser_visitor *visitor) override; expression& base(); - std::string& field(); + const identifier& field(); field_access_expression *is_field_access() override; @@ -649,7 +660,7 @@ namespace elna::boot expression *m_base; public: - dereference_expression(const struct position position, expression *base); + dereference_expression(const source_position position, expression *base); void accept(parser_visitor *visitor) override; expression& base(); @@ -669,7 +680,7 @@ namespace elna::boot public: const std::vector<expression *> arguments; - procedure_call(const struct position position, designator_expression *callable, + procedure_call(const source_position position, designator_expression *callable, std::vector<expression *>&& arguments); void accept(parser_visitor *visitor) override; virtual procedure_call *is_call_expression() override; @@ -690,7 +701,7 @@ namespace elna::boot * \param lvalue Left-hand side. * \param rvalue Assigned expression. */ - assign_statement(const struct position position, designator_expression *lvalue, + assign_statement(const source_position position, designator_expression *lvalue, expression *rvalue); void accept(parser_visitor *visitor) override; @@ -711,7 +722,7 @@ namespace elna::boot const std::vector<conditional_statements *> branches; const std::vector<statement *> *alternative; - if_statement(const struct position position, conditional_statements *branch, + if_statement(const source_position position, conditional_statements *branch, std::vector<conditional_statements *>&& branches, std::vector<statement *> *alternative = nullptr); void accept(parser_visitor *visitor) override; @@ -729,7 +740,7 @@ namespace elna::boot public: const std::vector<std::string> segments; - import_declaration(const struct position position, std::vector<std::string>&& segments); + import_declaration(const source_position position, std::vector<std::string>&& segments); void accept(parser_visitor *visitor) override; }; @@ -743,7 +754,7 @@ namespace elna::boot public: const std::vector<conditional_statements *> branches; - while_statement(const struct position position, conditional_statements *branch, + while_statement(const source_position position, conditional_statements *branch, std::vector<conditional_statements *>&& branches); void accept(parser_visitor *visitor) override; @@ -759,8 +770,8 @@ namespace elna::boot const std::vector<type_declaration *> types; const std::vector<procedure_declaration *> procedures; - unit(const struct position position); - unit(const struct position position, + unit(const source_position position); + unit(const source_position position, std::vector<import_declaration *>&& imports, std::vector<constant_declaration *>&& constants, std::vector<type_declaration *>&& types, @@ -779,7 +790,7 @@ namespace elna::boot public: T value; - literal(const struct position position, const T& value) + literal(const source_position position, const T& value) : node(position), value(value) { } @@ -795,7 +806,7 @@ namespace elna::boot public: const std::vector<statement *> statements; - defer_statement(const struct position position, std::vector<statement *>&& statements); + defer_statement(const source_position position, std::vector<statement *>&& statements); void accept(parser_visitor *visitor) override; virtual ~defer_statement() override; @@ -804,7 +815,7 @@ namespace elna::boot class empty_statement : public statement { public: - empty_statement(const struct position); + empty_statement(const source_position); void accept(parser_visitor *visitor) override; }; @@ -815,7 +826,7 @@ namespace elna::boot binary_operator m_operator; public: - binary_expression(const struct position position, expression *lhs, + binary_expression(const source_position position, expression *lhs, expression *rhs, const binary_operator operation); void accept(parser_visitor *visitor) override; @@ -834,7 +845,7 @@ namespace elna::boot unary_operator m_operator; public: - unary_expression(const struct position position, expression *operand, + unary_expression(const source_position position, expression *operand, const unary_operator operation); void accept(parser_visitor *visitor) override; diff --git a/include/elna/boot/driver.h b/include/elna/boot/driver.h index cd29376..8f93dbd 100644 --- a/include/elna/boot/driver.h +++ b/include/elna/boot/driver.h @@ -23,7 +23,7 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { - position make_position(const yy::location& location); + source_position make_position(const yy::location& location); class syntax_error final : public error { diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h index 0556dbe..953f701 100644 --- a/include/elna/boot/result.h +++ b/include/elna/boot/result.h @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #include <string> #include <deque> #include <memory> +#include <optional> #include <variant> namespace elna::boot @@ -30,29 +31,44 @@ namespace elna::boot */ struct location { - /// Line. - const std::size_t line; + location() = default; + location(const std::size_t line, const std::size_t column); - /// Column. - const std::size_t column; + /// \return Line. + std::size_t line() const; - location(std::size_t line, std::size_t column); + /// \return Column. + std::size_t column() const; bool available() const; + + bool operator==(const location& that) const; + bool operator!=(const location& that) const; + + private: + std::size_t m_line{ 0 }; + std::size_t m_column{ 0 }; }; /** * Span in the source text. */ - struct position + struct source_position { - /// Start location. - const location start; + source_position(location start, location end); + + /// \return Start location. + const location& start() const; - /// End location. - const location end; + /// \return End location. + const location& end() const; - position(location start, location end); + /// \return Whether this position spans more than one location. + bool is_span() const; + + private: + location m_start; + location m_end; }; /** @@ -61,14 +77,25 @@ namespace elna::boot class error { protected: - error(const struct position position); + error(const source_position position); public: /// Error position. - const struct position position; + const source_position position; /// Error text. virtual std::string what() const = 0; + + /** + * Supplementary context shown alongside the primary error, + * e.g.\ the location of a previous declaration. + * + * \return Optional note message and position. + */ + virtual std::optional<std::pair<std::string, source_position>> note() const + { + return std::nullopt; + } }; using error_list = typename std::deque<std::unique_ptr<error>>; @@ -111,27 +138,55 @@ namespace elna::boot { } - bool operator==(const return_declaration& other) const + bool operator==(const return_declaration& that) const + { + return this->proper_type == that.proper_type && this->no_return == that.no_return; + } + + bool operator!=(const return_declaration& that) const { - return this->proper_type == other.proper_type && this->no_return == other.no_return; + return !(*this == that); } T proper_type{}; bool no_return{ false }; }; - struct identifier_definition + struct identifier { - std::string name; - bool exported; + identifier(const std::string& name, const source_position& position); + + const std::string& name() const; + const source_position& position() const; + + bool operator==(const identifier& that) const; + bool operator!=(const identifier& that) const; - bool operator==(const identifier_definition& that) const; bool operator==(const std::string& that) const; + bool operator!=(const std::string& that) const; + + private: + std::string m_name; + source_position m_position; + }; + + struct identifier_definition + { + identifier_definition(const std::string& name, const source_position& position, + const bool exported); + + const std::string& name() const; + const identifier& id() const; + bool exported() const; + + private: + identifier m_identifier; + bool m_exported{ false }; }; } template<> -struct std::hash<elna::boot::identifier_definition> +struct std::hash<elna::boot::identifier> { - std::size_t operator()(const elna::boot::identifier_definition& key) const noexcept; + std::size_t operator()(const elna::boot::identifier& key) const noexcept; }; diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h index 4041f33..82ef5f1 100644 --- a/include/elna/boot/semantic.h +++ b/include/elna/boot/semantic.h @@ -19,7 +19,7 @@ along with GCC; see the file COPYING3. If not see #include <string> #include <memory> -#include <set> +#include <map> #include "elna/boot/ast.h" #include "elna/boot/result.h" @@ -32,57 +32,91 @@ namespace elna::boot */ class declaration_error : public error { - const std::string identifier; - public: enum class kind { undeclared, - already, local_export }; + private: + std::string identifier; + kind error_kind; + + public: + declaration_error(const kind error_kind, - const std::string& identifier, const struct position position); + const boot::identifier& identifier); std::string what() const override; - - private: - const kind error_kind; }; /** - * A symbol was already declared in this scope. + * Attempted to redefine a name that is already in use in the + * current scope. */ - class type_expectation_error : public error + class redefinition_error : public error { + std::string identifier; + std::optional<source_position> original; + public: - enum class kind - { - assignment, - result, - argument - }; + redefinition_error(const boot::identifier& identifier, + std::optional<source_position> original); - type_expectation_error(const kind error_kind, const struct position position); + std::string what() const override; + + std::optional<std::pair<std::string, source_position>> note() const override; + }; + + /** + * Expected type does not match the actual type of an expression. + */ + class type_mismatch_error : public error + { + type expected; + type actual; + + public: + type_mismatch_error(const source_position position, + type expected, type actual); std::string what() const override; + }; - private: - const kind error_kind; + /** + * Attempted to access a field that does not exist on the given type. + */ + class field_not_found_error : public error + { + std::string field_name; + type composite_type; + + public: + field_not_found_error(const identifier& field_name, + type composite_type); + + std::string what() const override; }; /** * Field with the same name is already declared in this type. */ - class field_duplication_error : public error + class duplicate_member_error : public error { - const std::string field_name; + std::string member_name; + type aggregate; + std::optional<source_position> original; + std::optional<std::string> base_name; public: - field_duplication_error(const std::string& field_name, const struct position position); + duplicate_member_error(const boot::identifier& member_name, + type aggregate, std::optional<source_position> original = std::nullopt, + std::optional<std::string> base_name = std::nullopt); std::string what() const override; + + std::optional<std::pair<std::string, source_position>> note() const override; }; /** @@ -90,10 +124,10 @@ namespace elna::boot */ class cyclic_declaration_error : public error { - const std::vector<std::string> cycle; + std::vector<std::string> cycle; public: - cyclic_declaration_error(const std::vector<std::string>& cycle, const struct position position); + cyclic_declaration_error(const std::vector<std::string>& cycle, const source_position position); std::string what() const override; }; @@ -103,10 +137,12 @@ namespace elna::boot */ class return_error : public error { - const std::string identifier; + std::string identifier; + type return_type; public: - return_error(const std::string& identifier, const struct position position); + return_error(const std::string& identifier, const source_position position, + type return_type = type()); std::string what() const override; }; @@ -116,10 +152,10 @@ namespace elna::boot */ class base_type_error : public error { - const std::string base; + type actual; public: - base_type_error(const std::string& base, const struct position position); + base_type_error(type actual, const source_position position); std::string what() const override; }; @@ -130,12 +166,12 @@ namespace elna::boot */ class argument_count_error : public error { - const std::size_t expected; - const std::size_t actual; + std::size_t expected; + std::size_t actual; public: argument_count_error(std::size_t expected, std::size_t actual, - const struct position position); + const source_position position); std::string what() const override; }; @@ -166,6 +202,15 @@ namespace elna::boot }; /** + * Origin of a field in a composite type. + */ + struct field_origin + { + std::optional<source_position> declaration; + type base_type; + }; + + /** * Performs name analysis. */ class name_analysis_visitor final : public walking_visitor, public error_container @@ -178,9 +223,10 @@ namespace elna::boot std::pair<procedure_type, std::vector<std::string>> build_procedure( procedure_type_expression& expression); std::vector<type_field> build_composite_type(const std::vector<field_declaration>& fields, - std::set<std::string>& known_names); + std::map<std::string, field_origin>& known_names, + type aggregate); std::shared_ptr<variable_info> register_variable(const std::string& name, - const bool is_extern, const struct position position); + const bool is_extern, const source_position position); type lookup_primitive_type(const std::string& name); type type_of_constant(const constant_info::variant& value); diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index e13e842..ec5a051 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -89,6 +89,12 @@ namespace elna::boot bool operator==(const type& other) const; bool empty() const; + + /** + * \return Human-readable representation of this type suitable for + * diagnostics, e.g. \c "Int", \c "^Char", \c "record ... end". + */ + std::string to_string() const; }; struct alias_type @@ -157,6 +163,7 @@ namespace elna::boot { public: bool exported{ false }; + std::optional<source_position> position; virtual ~info() = 0; diff --git a/include/elna/gcc/elna-diagnostic.h b/include/elna/gcc/elna-diagnostic.h index 3957003..31e180e 100644 --- a/include/elna/gcc/elna-diagnostic.h +++ b/include/elna/gcc/elna-diagnostic.h @@ -41,8 +41,8 @@ namespace elna::gcc ~linemap_guard(); }; - location_t get_location(const boot::position *position); - void fill_range(const boot::position& position, location_t& start, location_t& end); + location_t get_location(const boot::source_position *position); + location_t make_range(const boot::source_position& position); std::string print_type(tree type); void report_errors(const std::deque<std::unique_ptr<boot::error>>& errors); } |
