diff options
25 files changed, 558 insertions, 395 deletions
diff --git a/boot/ast.cc b/boot/ast.cc index 37e89ac..3454cce 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -29,6 +29,11 @@ namespace elna::boot __builtin_unreachable(); } + void empty_visitor::visit(constant_type_expression *) + { + __builtin_unreachable(); + } + void empty_visitor::visit(type_declaration *) { __builtin_unreachable(); @@ -54,11 +59,6 @@ namespace elna::boot __builtin_unreachable(); } - void empty_visitor::visit(constant_declaration *) - { - __builtin_unreachable(); - } - void empty_visitor::visit(procedure_declaration *) { __builtin_unreachable(); @@ -210,10 +210,6 @@ namespace elna::boot } if (declaration->body.has_value()) { - for (constant_declaration *const constant : declaration->body.value().constants) - { - constant->accept(this); - } for (variable_declaration *const variable : declaration->body.value().variables) { variable->accept(this); @@ -336,10 +332,6 @@ namespace elna::boot { variable->accept(this); } - for (constant_declaration *const constant : unit->constants) - { - constant->accept(this); - } for (procedure_declaration *const procedure : unit->procedures) { procedure->accept(this); @@ -364,17 +356,17 @@ namespace elna::boot } } - void walking_visitor::visit(constant_declaration *declaration) + void walking_visitor::visit(array_type_expression *expression) { - declaration->initializer().accept(this); + expression->base().accept(this); } - void walking_visitor::visit(array_type_expression *expression) + void walking_visitor::visit(pointer_type_expression *expression) { expression->base().accept(this); } - void walking_visitor::visit(pointer_type_expression *expression) + void walking_visitor::visit(constant_type_expression *expression) { expression->base().accept(this); } @@ -557,6 +549,11 @@ namespace elna::boot return nullptr; } + constant_type_expression *type_expression::is_constant() + { + return nullptr; + } + record_type_expression *type_expression::is_record() { return nullptr; @@ -624,6 +621,32 @@ namespace elna::boot return *m_base; } + constant_type_expression::constant_type_expression(const source_position position, + type_expression *base) + : node(position), m_base(base) + { + } + + constant_type_expression::~constant_type_expression() + { + delete m_base; + } + + void constant_type_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + constant_type_expression *constant_type_expression::is_constant() + { + return this; + } + + type_expression& constant_type_expression::base() + { + return *m_base; + } + record_type_expression::record_type_expression(const source_position position, std::vector<field_declaration>&& fields) : node(position), fields(std::move(fields)) @@ -753,27 +776,6 @@ namespace elna::boot { } - constant_declaration::constant_declaration(const source_position position, identifier_definition identifier, - expression *initializer) - : declaration(position, identifier), m_initializer(initializer) - { - } - - void constant_declaration::accept(parser_visitor *visitor) - { - visitor->visit(this); - } - - expression& constant_declaration::initializer() - { - return *m_initializer; - } - - constant_declaration::~constant_declaration() - { - delete m_initializer; - } - 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)) @@ -863,17 +865,15 @@ namespace elna::boot return *m_underlying_type; } - procedure_body::procedure_body(std::vector<constant_declaration *>&& constants, - std::vector<variable_declaration *>&& variables, std::vector<statement *>&& entry_point, - expression *return_expr) - : constants(std::move(constants)), variables(std::move(variables)), - entry_point(std::move(entry_point)), return_expression(return_expr) + procedure_body::procedure_body(std::vector<variable_declaration *>&& variables, + std::vector<statement *>&& entry_point, expression *return_expression) + : variables(std::move(variables)), + entry_point(std::move(entry_point)), return_expression(return_expression) { } procedure_body::procedure_body(procedure_body&& that) - : constants(std::move(const_cast<std::vector<constant_declaration *>&>(that.constants))), - variables(std::move(const_cast<std::vector<variable_declaration *>&>(that.variables))), + : variables(std::move(const_cast<std::vector<variable_declaration *>&>(that.variables))), entry_point(std::move(const_cast<std::vector<statement *>&>(that.entry_point))), return_expression(that.return_expression) { @@ -889,27 +889,22 @@ namespace elna::boot { delete variable; } - for (constant_declaration *constant : this->constants) - { - delete constant; - } } unit::unit(const source_position position) - : node(position), procedure_body(std::vector<constant_declaration *>{}, - std::vector<variable_declaration *>{}, std::vector<statement *>{}, nullptr) + : node(position), + procedure_body(std::vector<variable_declaration *>{}, std::vector<statement *>{}, nullptr) { } unit::unit(const source_position position, std::vector<import_declaration *>&& imports, - std::vector<constant_declaration *>&& constants, std::vector<type_declaration *>&& types, std::vector<variable_declaration *>&& variables, std::vector<procedure_declaration *>&& procedures, std::vector<statement *>&& entry_point) : node(position), - procedure_body(std::move(constants), std::move(variables), std::move(entry_point)), + procedure_body(std::move(variables), std::move(entry_point)), imports(std::move(imports)), types(std::move(types)), procedures(std::move(procedures)) { } diff --git a/boot/parser.yy b/boot/parser.yy index c34c905..1dd42a8 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -129,8 +129,6 @@ along with GCC; see the file COPYING3. If not see %type <std::vector<elna::boot::expression *>> case_labels; %type <elna::boot::switch_case> switch_case; %type <std::vector<elna::boot::switch_case>> switch_cases; -%type <elna::boot::constant_declaration *> constant_declaration; -%type <std::vector<elna::boot::constant_declaration *>> constant_part constant_declarations; %type <elna::boot::variable_declaration *> variable_declaration; %type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part; %type <elna::boot::type_expression *> type_expression; @@ -166,14 +164,14 @@ along with GCC; see the file COPYING3. If not see %type <std::vector<elna::boot::import_declaration *>> import_declarations import_part; %% program: - import_part constant_part type_part variable_part procedure_part statement_part "end" "." + import_part type_part variable_part procedure_part statement_part "end" "." { - boot::unit *tree = new boot::unit(boot::make_position(@$), $1, $2, $3, $4, $5, $6); + boot::unit *tree = new boot::unit(boot::make_position(@$), $1, $2, $3, $4, $5); driver.tree.reset(tree); } procedure_body: - constant_part variable_part statement_part procedure_return - { $$ = std::make_unique<boot::procedure_body>($1, $2, $3, $4); } + variable_part statement_part procedure_return + { $$ = std::make_unique<boot::procedure_body>($1, $2, $3); } statement_part: /* no statements */ {} @@ -444,6 +442,10 @@ type_expression: { $$ = new boot::array_type_expression(boot::make_position(@$), $4, $2); } + | "const" type_expression + { + $$ = new boot::constant_type_expression(boot::make_position(@$), $2); + } | "^" type_expression { $$ = new boot::pointer_type_expression(boot::make_position(@$), $2); @@ -501,20 +503,6 @@ variable_declarations: variable_part: /* no variable declarations */ {} | "var" variable_declarations { $$ = $2; } -constant_declaration: identifier_definition ":=" expression - { - $$ = new boot::constant_declaration(boot::make_position(@$), std::move(*$1), $3); - } -constant_declarations: - constant_declaration constant_declarations - { - $$ = $2; - $$.insert(std::cbegin($$), $1); - } - | /* no constant definitions */ {} -constant_part: - /* no constant definitions */ {} - | "const" constant_declarations { $$ = $2; } import_declaration: IDENTIFIER "." import_declaration { diff --git a/boot/semantic.cc b/boot/semantic.cc index 9137053..c987cfd 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -73,6 +73,18 @@ namespace elna::boot + "', but got '" + actual.to_string() + "'"; } + constant_assignment_error::constant_assignment_error(const source_position position, + type assignee) + : error(position), assignee(assignee) + { + } + + std::string constant_assignment_error::what() const + { + return "Cannot assign to a value of type '" + assignee.to_string() + + "', because it is constant or contains constant members"; + } + 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) @@ -81,7 +93,7 @@ namespace elna::boot std::string field_not_found_error::what() const { - type resolved = inner_aliased_type(composite_type); + type resolved = resolve_underlying_type(composite_type); bool is_enum = resolved.get<enumeration_type>() != nullptr; bool is_record = resolved.get<record_type>() != nullptr; @@ -112,7 +124,7 @@ namespace elna::boot std::string duplicate_member_error::what() const { - type resolved = inner_aliased_type(aggregate); + type resolved = resolve_underlying_type(aggregate); bool is_enum = resolved.get<enumeration_type>() != nullptr; std::string kind = is_enum ? "member" : "field"; std::string message = is_enum ? "Enumeration" : "Record"; @@ -208,7 +220,7 @@ namespace elna::boot unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait, type actual) - : error(trait.position()), trait_name(trait.name()), actual(actual) + : error(trait.position()), actual(actual), trait_name(trait.name()) { } @@ -218,6 +230,119 @@ namespace elna::boot + "' does not support trait '#" + trait_name + "'"; } + // Members of a constant aggregate are constant themselves. + static type qualify_member_type(const type& element, const type& aggregate) + { + if (resolve_aliases(aggregate).get<constant_type>() != nullptr + && resolve_aliases(element).get<constant_type>() == nullptr) + { + return type(std::make_shared<constant_type>(element)); + } + else + { + return element; + } + } + + /* + * Whether the type itself is constant or has a constant member at any + * nesting level, so that values of this type cannot be reassigned as a + * whole. Pointers to constants do not make the type itself constant. + */ + static bool contains_constant_member(const type& checked) + { + auto referent = resolve_aliases(checked); + + if (referent.get<constant_type>() != nullptr) + { + return true; + } + else if (auto record = referent.get<record_type>()) + { + for (const type_field& field : record->fields) + { + if (contains_constant_member(field.second)) + { + return true; + } + } + return !record->base.empty() && contains_constant_member(record->base); + } + else if (auto array = referent.get<array_type>()) + { + return contains_constant_member(array->base); + } + return false; + } + + bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias, + std::vector<std::string>& alias_path) + { + if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path)) + { + return false; + } + alias_path.push_back(alias->name); + + if (auto another_alias = alias->reference.get<alias_type>()) + { + return check_unresolved_symbol(another_alias, alias_path); + } + return true; + } + + bool type_analysis_visitor::is_base_of(const std::shared_ptr<record_type>& base, + const std::shared_ptr<record_type>& derived) + { + if (derived != nullptr) + { + if (auto current_record = resolve_underlying_type(derived->base).get<record_type>()) + { + return current_record == base || is_base_of(base, current_record); + } + } + return false; + } + + bool type_analysis_visitor::is_assignable_from(const type& assignee, const type& assignment) + { + type resolved_assignee = resolve_underlying_type(assignee); + type resolved_assignment = resolve_underlying_type(assignment); + + if (resolved_assignee == resolved_assignment + || (is_primitive_type(resolved_assignee, "Pointer") && is_any_pointer_type(resolved_assignment)) + || (is_primitive_type(resolved_assignment, "Pointer") && is_any_pointer_type(resolved_assignee))) + { + return true; + } + std::shared_ptr<pointer_type> assignee_pointer = resolved_assignee.get<pointer_type>(); + std::shared_ptr<pointer_type> assignment_pointer = resolved_assignment.get<pointer_type>(); + + if (assignee_pointer == nullptr || assignment_pointer == nullptr) + { + return false; + } + auto assignee_pointee_const = resolve_aliases(assignee_pointer->base).get<constant_type>(); + auto assignment_pointee_const = resolve_aliases(assignment_pointer->base).get<constant_type>(); + + // Constness can be added at the first indirection level, but not removed. + if (assignee_pointee_const != nullptr + && assignee_pointee_const->unqualified == assignment_pointer->base) + { + return true; + } + if (assignment_pointee_const != nullptr && assignee_pointee_const == nullptr) + { + return false; + } + // A pointer to a record can be assigned to a pointer to its base type. + std::shared_ptr<record_type> assignee_record = + resolve_underlying_type(assignee_pointer->base).get<record_type>(); + + return assignee_record != nullptr + && is_base_of(assignee_record, resolve_underlying_type(assignment_pointer->base).get<record_type>()); + } + type_analysis_visitor::type_analysis_visitor(symbol_bag bag) : error_container(), bag(bag) { @@ -272,7 +397,12 @@ namespace elna::boot { walking_visitor::visit(statement); - if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration)) + if (contains_constant_member(statement->lvalue().type_decoration)) + { + add_error<constant_assignment_error>(statement->position(), + statement->lvalue().type_decoration); + } + else if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration)) { add_error<type_mismatch_error>(statement->position(), statement->lvalue().type_decoration, statement->rvalue().type_decoration); @@ -302,7 +432,7 @@ namespace elna::boot void type_analysis_visitor::visit(case_statement *statement) { walking_visitor::visit(statement); - type condition_type = inner_aliased_type(statement->condition().type_decoration); + type condition_type = resolve_underlying_type(statement->condition().type_decoration); for (const switch_case& case_block : statement->cases) { @@ -317,22 +447,6 @@ namespace elna::boot } } - bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias, - std::vector<std::string>& alias_path) - { - if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path)) - { - return false; - } - alias_path.push_back(alias->name); - - if (auto another_alias = alias->reference.get<alias_type>()) - { - return check_unresolved_symbol(another_alias, alias_path); - } - return true; - } - void type_analysis_visitor::visit(type_declaration *declaration) { std::vector<std::string> alias_path; @@ -352,7 +466,7 @@ namespace elna::boot { if (expression->base.has_value()) { - type base_type = inner_aliased_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol); + type base_type = resolve_underlying_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol); if (base_type.get<record_type>() == nullptr) { add_error<base_type_error>(base_type, expression->position()); @@ -393,7 +507,7 @@ namespace elna::boot void type_analysis_visitor::visit(record_constructor_expression *expression) { - auto record = inner_aliased_type(expression->type_decoration).get<record_type>(); + auto record = resolve_underlying_type(expression->type_decoration).get<record_type>(); if (record == nullptr) { @@ -422,7 +536,7 @@ namespace elna::boot void type_analysis_visitor::visit(array_constructor_expression *expression) { - auto array = inner_aliased_type(expression->type_decoration).get<array_type>(); + auto array = resolve_underlying_type(expression->type_decoration).get<array_type>(); if (array == nullptr) { @@ -490,49 +604,9 @@ namespace elna::boot return this->bag.lookup(name)->is_type()->symbol; } - type name_analysis_visitor::type_of_constant(const constant_info::variant& value) - { - return std::visit([this](auto&& arg) -> type { - using T = std::decay_t<decltype(arg)>; - - if constexpr (std::is_same_v<T, std::int32_t>) - { - return lookup_primitive_type("Int"); - } - else if constexpr (std::is_same_v<T, std::uint32_t>) - { - return lookup_primitive_type("Word"); - } - else if constexpr (std::is_same_v<T, double>) - { - return lookup_primitive_type("Float"); - } - else if constexpr (std::is_same_v<T, bool>) - { - return lookup_primitive_type("Bool"); - } - else if constexpr (std::is_same_v<T, unsigned char>) - { - return lookup_primitive_type("Char"); - } - else if constexpr (std::is_same_v<T, std::nullptr_t>) - { - return lookup_primitive_type("Pointer"); - } - else if constexpr (std::is_same_v<T, std::string>) - { - return lookup_primitive_type("String"); - } - else - { - static_assert(!sizeof(T*), "Unhandled constant type"); - } - }, value); - } - type name_analysis_visitor::lookup_field(const type& composite_type, const std::string& field_name) { - type resolved_type = inner_aliased_type(composite_type); + type resolved_type = resolve_underlying_type(composite_type); if (auto record = resolved_type.get<record_type>()) { @@ -579,6 +653,12 @@ namespace elna::boot this->current_type = type(std::make_shared<pointer_type>(this->current_type)); } + void name_analysis_visitor::visit(constant_type_expression *expression) + { + walking_visitor::visit(expression); + this->current_type = type(std::make_shared<constant_type>(this->current_type)); + } + void name_analysis_visitor::visit(array_type_expression *expression) { walking_visitor::visit(expression); @@ -593,7 +673,7 @@ namespace elna::boot 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>(); + auto record = resolve_underlying_type(composite_type).get<record_type>(); if (record == nullptr) { return; @@ -803,15 +883,6 @@ namespace elna::boot } } - void name_analysis_visitor::visit(constant_declaration *declaration) - { - walking_visitor::visit(declaration); - auto constant_symbol = std::make_shared<constant_info>(this->current_literal); - - 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) { @@ -833,10 +904,6 @@ namespace elna::boot ++name_iterator; ++type_iterator; } - for (constant_declaration *const constant : declaration->body.value().constants) - { - constant->accept(this); - } for (variable_declaration *const variable : declaration->body.value().variables) { variable->accept(this); @@ -887,10 +954,6 @@ namespace elna::boot { variable->accept(this); } - for (constant_declaration *const constant : unit->constants) - { - constant->accept(this); - } for (procedure_declaration *const procedure : unit->procedures) { procedure->accept(this); @@ -932,7 +995,7 @@ namespace elna::boot if (!trait->type_decoration.empty()) { - type resolved = inner_aliased_type(trait->type_decoration); + type resolved = resolve_underlying_type(trait->type_decoration); bool is_enum = resolved.get<enumeration_type>() != nullptr; bool is_integral = false; @@ -1005,7 +1068,7 @@ namespace elna::boot void name_analysis_visitor::visit(array_access_expression *expression) { walking_visitor::visit(expression); - auto resolved_base= inner_aliased_type(expression->base().type_decoration); + auto resolved_base = resolve_underlying_type(expression->base().type_decoration); if (auto array = resolved_base.get<array_type>()) { @@ -1015,6 +1078,13 @@ namespace elna::boot { expression->type_decoration = lookup_primitive_type("Char"); } + // Elements of a constant array are constant themselves since a static + // array is a holistic type. + if (!expression->type_decoration.empty()) + { + expression->type_decoration = qualify_member_type(expression->type_decoration, + expression->base().type_decoration); + } } void name_analysis_visitor::visit(field_access_expression *expression) @@ -1029,7 +1099,11 @@ namespace elna::boot } if (expression->type_decoration.empty()) { - add_error<field_not_found_error>(expression->field(), + add_error<field_not_found_error>(expression->field(), expression->base().type_decoration); + } + else + { + expression->type_decoration = qualify_member_type(expression->type_decoration, expression->base().type_decoration); } } @@ -1038,7 +1112,7 @@ namespace elna::boot { walking_visitor::visit(expression); - if (auto pointer = inner_aliased_type(expression->base().type_decoration).get<pointer_type>()) + if (auto pointer = resolve_underlying_type(expression->base().type_decoration).get<pointer_type>()) { expression->type_decoration = pointer->base; } @@ -1068,10 +1142,6 @@ namespace elna::boot { expression->type_decoration = variable_symbol->symbol; } - else if (auto constant_symbol = from_symbol_table->is_constant()) - { - expression->type_decoration = type_of_constant(constant_symbol->symbol); - } else if (auto procedure_symbol = from_symbol_table->is_procedure()) { expression->type_decoration = type(std::make_shared<procedure_type>(procedure_symbol->symbol)); @@ -1086,43 +1156,36 @@ namespace elna::boot void name_analysis_visitor::visit(literal<std::int32_t> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("Int"); } void name_analysis_visitor::visit(literal<std::uint32_t> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("Word"); } void name_analysis_visitor::visit(literal<double> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("Float"); } void name_analysis_visitor::visit(literal<bool> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("Bool"); } void name_analysis_visitor::visit(literal<unsigned char> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("Char"); } void name_analysis_visitor::visit(literal<std::nullptr_t> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("Pointer"); } void name_analysis_visitor::visit(literal<std::string> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("String"); } @@ -1168,10 +1231,6 @@ namespace elna::boot { return; } - for (constant_declaration *const constant : declaration->body.value().constants) - { - constant->accept(this); - } for (variable_declaration *const variable : declaration->body.value().variables) { variable->accept(this); @@ -1189,79 +1248,4 @@ namespace elna::boot } } } - - void declaration_visitor::visit(constant_declaration *declaration) - { - if (declaration->identifier.exported()) - { - add_error<declaration_error>(declaration_error::kind::local_export, - declaration->identifier.id()); - } - } - - bool is_base_of(std::shared_ptr<record_type> base, std::shared_ptr<record_type> derived) - { - type current = derived->base; - - while (!current.empty()) - { - auto current_record = inner_aliased_type(current).get<record_type>(); - if (current_record == nullptr) - { - return false; - } - if (current_record == base) - { - return true; - } - current = current_record->base; - } - return false; - } - - bool are_compatible_pointers(type lhs_type, type rhs_type) - { - std::shared_ptr<primitive_type> lhs_primitive = lhs_type.get<primitive_type>(); - std::shared_ptr<primitive_type> rhs_primitive = rhs_type.get<primitive_type>(); - - bool lhs_is_primitive_pointer = lhs_primitive != nullptr && lhs_primitive->identifier == "Pointer"; - bool rhs_is_primitive_pointer = rhs_primitive != nullptr && rhs_primitive->identifier == "Pointer"; - - return (lhs_is_primitive_pointer && rhs_type.get<pointer_type>() != nullptr) - || (rhs_is_primitive_pointer && lhs_type.get<pointer_type>() != nullptr) - || (lhs_is_primitive_pointer && rhs_type.get<procedure_type>() != nullptr) - || (rhs_is_primitive_pointer && lhs_type.get<procedure_type>() != nullptr) - || (lhs_is_primitive_pointer && rhs_is_primitive_pointer); - } - - bool is_assignable_from(const type& assignee, const type& assignment) - { - type resolved_assignee = inner_aliased_type(assignee); - type resolved_assignment = inner_aliased_type(assignment); - - if (resolved_assignee == resolved_assignment) - { - return true; - } - // If the types do not match, only some pointers can be assigned. - if (are_compatible_pointers(resolved_assignee, resolved_assignment)) - { - return true; - } - std::shared_ptr<pointer_type> assignee_pointer = resolved_assignee.get<pointer_type>(); - std::shared_ptr<pointer_type> assignment_pointer = resolved_assignment.get<pointer_type>(); - - if (assignee_pointer == nullptr || assignment_pointer == nullptr) - { - return false; - } - // A pointer to a record can be assigned to a pointer to its base type. - type assignee_pointee = inner_aliased_type(assignee_pointer->base); - type assignment_pointee = inner_aliased_type(assignment_pointer->base); - std::shared_ptr<record_type> assignee_record = assignee_pointee.get<record_type>(); - std::shared_ptr<record_type> assignment_record = assignment_pointee.get<record_type>(); - - return assignee_record != nullptr && assignment_record != nullptr - && is_base_of(assignee_record, assignment_record); - } } diff --git a/boot/symbol.cc b/boot/symbol.cc index a3a36c1..9985489 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -43,6 +43,11 @@ namespace elna::boot { } + type::type(std::shared_ptr<constant_type> constant) + : tag(type_tag::constant), constant(constant) + { + } + type::type(std::shared_ptr<array_type> array) : tag(type_tag::array), array(array) { @@ -76,6 +81,9 @@ namespace elna::boot case type_tag::pointer: new (&pointer) std::shared_ptr<pointer_type>(other.pointer); break; + case type_tag::constant: + new (&constant) std::shared_ptr<constant_type>(other.constant); + break; case type_tag::array: new (&array) std::shared_ptr<array_type>(other.array); break; @@ -112,6 +120,9 @@ namespace elna::boot case type_tag::pointer: new (&pointer) std::shared_ptr<pointer_type>(std::move(other.pointer)); break; + case type_tag::constant: + new (&constant) std::shared_ptr<constant_type>(std::move(other.constant)); + break; case type_tag::array: new (&array) std::shared_ptr<array_type>(std::move(other.array)); break; @@ -164,6 +175,9 @@ namespace elna::boot case type_tag::pointer: this->pointer.~shared_ptr<pointer_type>(); break; + case type_tag::constant: + this->constant.~shared_ptr<constant_type>(); + break; case type_tag::array: this->array.~shared_ptr<array_type>(); break; @@ -207,6 +221,12 @@ namespace elna::boot } template<> + std::shared_ptr<constant_type> type::get<constant_type>() const + { + return tag == type_tag::constant ? this->constant : nullptr; + } + + template<> std::shared_ptr<procedure_type> type::get<procedure_type>() const { return tag == type_tag::procedure ? this->procedure : nullptr; @@ -225,8 +245,8 @@ namespace elna::boot bool type::operator==(const type& other) const { - type resolved_this = inner_aliased_type(*this); - type resolved_that = inner_aliased_type(other); + type resolved_this = resolve_aliases(*this); + type resolved_that = resolve_aliases(other); if (auto left_record = resolved_this.get<record_type>()) { @@ -248,6 +268,12 @@ namespace elna::boot return right_pointer != nullptr && left_pointer->base == right_pointer->base; } + if (auto left_const = resolved_this.get<constant_type>()) + { + auto right_const = resolved_that.get<constant_type>(); + + return right_const != nullptr && left_const->unqualified == right_const->unqualified; + } if (auto left_array = resolved_this.get<array_type>()) { auto right_array = resolved_that.get<array_type>(); @@ -282,6 +308,8 @@ namespace elna::boot return primitive->identifier; case type_tag::pointer: return "^" + pointer->base.to_string(); + case type_tag::constant: + return "const " + constant->unqualified.to_string(); case type_tag::array: return "[" + std::to_string(array->size) + "]" + array->base.to_string(); case type_tag::record: @@ -328,6 +356,11 @@ namespace elna::boot { } + constant_type::constant_type(type unqualified) + : unqualified(unqualified) + { + } + array_type::array_type(type base, std::uint64_t size) : base(base), size(size) { @@ -367,11 +400,6 @@ namespace elna::boot return nullptr; } - std::shared_ptr<constant_info> info::is_constant() - { - return nullptr; - } - std::shared_ptr<variable_info> info::is_variable() { return nullptr; @@ -403,16 +431,6 @@ namespace elna::boot return this->scope == nullptr; } - constant_info::constant_info(const variant& symbol) - : symbol(symbol) - { - } - - std::shared_ptr<constant_info> constant_info::is_constant() - { - return std::static_pointer_cast<constant_info>(shared_from_this()); - } - variable_info::variable_info(const type symbol, bool is_extern) : symbol(symbol), is_extern(is_extern) { @@ -524,20 +542,47 @@ namespace elna::boot return m_exported; } - type inner_aliased_type(std::shared_ptr<alias_type> alias) + type resolve_underlying_type(std::shared_ptr<alias_type> alias) { - return inner_aliased_type(alias->reference); + return resolve_underlying_type(alias->reference); } - type inner_aliased_type(const type& alias) + type resolve_underlying_type(const type& alias) { - if (auto aliased = alias.get<alias_type>()) + type resolved_alias = resolve_aliases(alias); + + if (auto qualified = resolved_alias.get<constant_type>()) { - return inner_aliased_type(aliased); + return resolve_underlying_type(qualified->unqualified); } else { - return alias; + return resolved_alias; + } + } + + type resolve_aliases(const type& checked) + { + if (auto alias = checked.get<alias_type>()) + { + return resolve_aliases(alias->reference); + } + return checked; + } + + bool is_primitive_type(const type& checked, const std::string& name) + { + if (auto primitive_checked = checked.get<primitive_type>()) + { + return primitive_checked->identifier == name; } + return false; + } + + bool is_any_pointer_type(const type& checked) + { + return checked.get<pointer_type>() != nullptr + || checked.get<procedure_type>() != nullptr + || is_primitive_type(checked, "Pointer"); } } diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc index 9e331cf..3c424f8 100644 --- a/gcc/gcc/elna-builtins.cc +++ b/gcc/gcc/elna-builtins.cc @@ -160,6 +160,10 @@ namespace elna::gcc return build_pointer_type(procedure); } + else if (auto reference = type.get<boot::constant_type>()) + { + return get_inner_alias(reference->unqualified, symbols); + } else if (auto reference = type.get<boot::alias_type>()) { return TREE_TYPE(handle_symbol(reference->name, reference, symbols)); diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index 4067bb6..b849715 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -228,10 +228,6 @@ namespace elna::gcc { declaration->accept(this); } - for (boot::constant_declaration *const constant : unit->constants) - { - constant->accept(this); - } for (boot::variable_declaration *const variable : unit->variables) { variable->accept(this); @@ -309,10 +305,6 @@ namespace elna::gcc { this->symbols->enter(IDENTIFIER_POINTER(DECL_NAME(argument_chain)), argument_chain); } - for (boot::constant_declaration *const constant : declaration->body.value().constants) - { - constant->accept(this); - } for (boot::variable_declaration *const variable : declaration->body.value().variables) { variable->accept(this); @@ -704,41 +696,6 @@ namespace elna::gcc } } - void generic_visitor::visit(boot::constant_declaration *declaration) - { - location_t definition_location = get_location(&declaration->position()); - declaration->initializer().accept(this); - - if (assert_constant(definition_location)) - { - this->current_expression = fold_init(this->current_expression); - } - else - { - this->current_expression = NULL_TREE; - 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); - - 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(); - - if (!lang_hooks.decls.global_bindings_p()) - { - auto declaration_statement = build1_loc(definition_location, DECL_EXPR, - void_type_node, definition_tree); - append_statement(declaration_statement); - } - } - this->current_expression = NULL_TREE; - } - void generic_visitor::visit(boot::variable_declaration *declaration) { for (const auto& variable_identifier : declaration->identifiers) @@ -762,6 +719,14 @@ namespace elna::gcc { DECL_INITIAL(declaration_tree) = elna_pointer_nil_node; } + { + auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable(); + + if (variable_symbol != nullptr && variable_symbol->symbol.get<boot::constant_type>() != nullptr) + { + TREE_READONLY(declaration_tree) = 1; + } + } this->current_expression = NULL_TREE; if (lang_hooks.decls.global_bindings_p()) diff --git a/gcc/testlib/elna-dg.exp b/gcc/testlib/elna-dg.exp index ddebf58..5142884 100644 --- a/gcc/testlib/elna-dg.exp +++ b/gcc/testlib/elna-dg.exp @@ -35,6 +35,47 @@ proc elna-dg-prune { system text } { # Utility routines. # +# Escapes a directive argument so that it reaches dejagnu intact. +# +# dg.exp evaluates each extracted directive as a Tcl script +# ("catch $op"), which performs one round of backslash, command and +# variable substitution inside the double-quoted argument. Quoting the +# affected characters here means the argument dejagnu receives is +# exactly what the test author wrote, so @Error messages keep their +# regular expression semantics: "\[2\]" matches literal brackets and +# "[2]" stays a character class. +# +proc elna-escape-directive { text } { + return [string map [list \ + "\\" "\\\\" \ + "\[" "\\\[" \ + "\]" "\\\]" \ + "\$" "\\\$" \ + "\"" "\\\""] $text] +} + +# +# Replaces an elna-native directive in the line with its dg-* equivalent, +# escaping the directive argument for dejagnu's Tcl evaluation. +# +# line_var - name of the variable holding the line in the caller. +# directive - elna directive name, e.g. @Error. +# dg_name - dejagnu directive name, e.g. dg-error. +# +proc elna-convert-directive { line_var directive dg_name } { + upvar 1 $line_var line + + if { [regexp -indices "\\(\\*\\s*$directive\\s+(.*?)\\s*\\*\\)" $line whole argument] } { + set escaped [elna-escape-directive \ + [string range $line [lindex $argument 0] [lindex $argument 1]]] + set prefix [string range $line 0 [expr {[lindex $whole 0] - 1}]] + set suffix [string range $line [expr {[lindex $whole 1] + 1}] end] + + set line "${prefix}(* { $dg_name \"$escaped\" } *)$suffix" + } +} + +# # This procedure copies a test file from the source tree to the # build directory while rewriting elna-native directives to dejagnu # dg-* equivalents. @@ -60,8 +101,8 @@ proc elna-convert-test { base test } { # Reads the source test file line by line and replace directives with # Elna equivalents using a regular expression. while { [gets $fin line] >= 0 } { - regsub -all {\(\*\s*@Error\s+(.*?)\s*\*\)} $line {(* { dg-error "\1" } *)} line - regsub -all {\(\*\s*@Flags\s+(.*?)\s*\*\)} $line {(* { dg-additional-options "\1" } *)} line + elna-convert-directive line @Error dg-error + elna-convert-directive line @Flags dg-additional-options puts $fout $line } diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index b6aad70..42e7666 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -55,7 +55,6 @@ namespace elna::boot }; class variable_declaration; - class constant_declaration; class procedure_declaration; class type_declaration; class procedure_call; @@ -74,6 +73,7 @@ namespace elna::boot class type_expression; class array_type_expression; class pointer_type_expression; + class constant_type_expression; class record_type_expression; class procedure_type_expression; class enumeration_type_expression; @@ -94,7 +94,6 @@ namespace elna::boot struct parser_visitor { virtual void visit(variable_declaration *) = 0; - virtual void visit(constant_declaration *) = 0; virtual void visit(procedure_declaration *) = 0; virtual void visit(type_declaration *) = 0; virtual void visit(procedure_call *) = 0; @@ -114,6 +113,7 @@ namespace elna::boot virtual void visit(unary_expression *) = 0; virtual void visit(array_type_expression *) = 0; virtual void visit(pointer_type_expression *) = 0; + virtual void visit(constant_type_expression *) = 0; virtual void visit(record_type_expression *) = 0; virtual void visit(procedure_type_expression *) = 0; virtual void visit(enumeration_type_expression *) = 0; @@ -138,13 +138,13 @@ namespace elna::boot public: [[noreturn]] virtual void visit(array_type_expression *) override; [[noreturn]] virtual void visit(pointer_type_expression *) override; + [[noreturn]] virtual void visit(constant_type_expression *) override; [[noreturn]] virtual void visit(type_declaration *) override; [[noreturn]] virtual void visit(record_type_expression *) override; [[noreturn]] virtual void visit(procedure_type_expression *) override; [[noreturn]] virtual void visit(enumeration_type_expression *) override; [[noreturn]] virtual void visit(variable_declaration *) override; - [[noreturn]] virtual void visit(constant_declaration *) override; [[noreturn]] virtual void visit(procedure_declaration *) override; [[noreturn]] virtual void visit(assign_statement *) override; [[noreturn]] virtual void visit(if_statement *) override; @@ -182,13 +182,13 @@ namespace elna::boot public: virtual void visit(array_type_expression *) override; virtual void visit(pointer_type_expression *) override; + virtual void visit(constant_type_expression *) override; virtual void visit(type_declaration *) override; virtual void visit(record_type_expression *) override; virtual void visit(procedure_type_expression *) override; virtual void visit(enumeration_type_expression *) override; virtual void visit(variable_declaration *) override; - virtual void visit(constant_declaration *) override; virtual void visit(procedure_declaration *) override; virtual void visit(assign_statement *) override; virtual void visit(if_statement *) override; @@ -280,6 +280,7 @@ namespace elna::boot virtual named_expression *is_named(); virtual array_type_expression *is_array(); virtual pointer_type_expression *is_pointer(); + virtual constant_type_expression *is_constant(); virtual record_type_expression *is_record(); virtual procedure_type_expression *is_procedure(); virtual enumeration_type_expression *is_enumeration(); @@ -316,6 +317,20 @@ namespace elna::boot type_expression& base(); }; + class constant_type_expression : public type_expression + { + type_expression *m_base; + + public: + constant_type_expression(const source_position position, type_expression *base); + ~constant_type_expression() override; + + void accept(parser_visitor *visitor) override; + constant_type_expression *is_constant() override; + + type_expression& base(); + }; + using field_declaration = std::pair<std::vector<identifier>, std::shared_ptr<type_expression>>; class record_type_expression : public type_expression @@ -430,23 +445,6 @@ namespace elna::boot }; /** - * Constant definition. - */ - class constant_declaration : public declaration - { - expression *m_initializer; - - public: - constant_declaration(const source_position position, identifier_definition identifier, - expression *initializer); - void accept(parser_visitor *visitor) override; - - expression& initializer(); - - virtual ~constant_declaration() override; - }; - - /** * Procedure type. */ class procedure_type_expression : public type_expression @@ -467,15 +465,13 @@ namespace elna::boot struct procedure_body { - const std::vector<constant_declaration *> constants; const std::vector<variable_declaration *> variables; const std::vector<statement *> entry_point; expression *const return_expression{ nullptr }; - procedure_body(std::vector<constant_declaration *>&& constants, - std::vector<variable_declaration *>&& variables, + procedure_body(std::vector<variable_declaration *>&& variables, std::vector<statement *>&& entry_point, - expression *return_expr = nullptr); + expression *return_expression = nullptr); procedure_body(const procedure_body&) = delete; procedure_body(procedure_body&& that); @@ -773,7 +769,6 @@ namespace elna::boot 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, std::vector<variable_declaration *>&& variables, std::vector<procedure_declaration *>&& procedures, diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h index 0f8f6c7..3d2de10 100644 --- a/include/elna/boot/semantic.h +++ b/include/elna/boot/semantic.h @@ -85,6 +85,19 @@ namespace elna::boot }; /** + * Attempted to assign a value whose type contains constant members. + */ + class constant_assignment_error : public error + { + type assignee; + + public: + constant_assignment_error(const source_position position, type assignee); + + std::string what() const override; + }; + + /** * Attempted to access a field that does not exist on the given type. */ class field_not_found_error : public error @@ -199,8 +212,20 @@ namespace elna::boot symbol_bag bag; std::shared_ptr<procedure_info> current_procedure; - bool check_unresolved_symbol(std::shared_ptr<alias_type> alias, + /* + * Whether an expression of type assignment can be assigned to a variable + * of type assignee. + */ + static bool is_assignable_from(const type& assignee, const type& assignment); + /* + * Checks whether derived has base in its parent chain. + * base should not be null, derived can be null. + */ + static bool is_base_of(const std::shared_ptr<record_type>& base, + const std::shared_ptr<record_type>& derived); + static bool check_unresolved_symbol(std::shared_ptr<alias_type> alias, std::vector<std::string>& path); + public: explicit type_analysis_visitor(symbol_bag bag); @@ -231,7 +256,6 @@ namespace elna::boot class name_analysis_visitor final : public walking_visitor, public error_container { type current_type; - constant_info::variant current_literal; symbol_bag bag; @@ -244,7 +268,6 @@ namespace elna::boot 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); type lookup_field(const type& composite_type, const std::string& field_name); public: @@ -252,6 +275,7 @@ namespace elna::boot void visit(array_type_expression *expression) override; void visit(pointer_type_expression *expression) override; + void visit(constant_type_expression *expression) override; void visit(type_declaration *declaration) override; void visit(record_type_expression *expression) override; void visit(record_constructor_expression *expression) override; @@ -260,7 +284,6 @@ namespace elna::boot void visit(enumeration_type_expression *expression) override; void visit(variable_declaration *declaration) override; - void visit(constant_declaration *declaration) override; void visit(procedure_declaration *declaration) override; void visit(procedure_call *call) override; @@ -297,16 +320,5 @@ namespace elna::boot void visit(type_declaration *declaration) override; void visit(procedure_declaration *declaration) override; void visit(variable_declaration *declaration) override; - void visit(constant_declaration *declaration) override; }; - - /** - * Whether an expression of type \a assignment can be assigned to a variable - * of type \a assignee. - * - * \param assignee Assignee. - * \param assignment Assignment. - * \return Whether the assignment can be performed. - */ - bool is_assignable_from(const type& assignee, const type& assignment); } diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index ec5a051..6884dd7 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -32,6 +32,7 @@ namespace elna::boot class primitive_type; class record_type; class pointer_type; + class constant_type; class array_type; class procedure_type; class enumeration_type; @@ -45,6 +46,7 @@ namespace elna::boot primitive, record, pointer, + constant, array, procedure, enumeration @@ -56,6 +58,7 @@ namespace elna::boot std::shared_ptr<primitive_type> primitive; std::shared_ptr<record_type> record; std::shared_ptr<pointer_type> pointer; + std::shared_ptr<constant_type> constant; std::shared_ptr<array_type> array; std::shared_ptr<procedure_type> procedure; std::shared_ptr<enumeration_type> enumeration; @@ -70,6 +73,7 @@ namespace elna::boot explicit type(std::shared_ptr<primitive_type> primitive); explicit type(std::shared_ptr<record_type> record); explicit type(std::shared_ptr<pointer_type> pointer); + explicit type(std::shared_ptr<constant_type> constant); explicit type(std::shared_ptr<array_type> array); explicit type(std::shared_ptr<procedure_type> procedure); explicit type(std::shared_ptr<enumeration_type> enumeration); @@ -112,6 +116,13 @@ namespace elna::boot explicit pointer_type(type base); }; + struct constant_type + { + const type unqualified; + + explicit constant_type(type unqualified); + }; + struct array_type { const type base; @@ -156,7 +167,6 @@ namespace elna::boot class type_info; class procedure_info; - class constant_info; class variable_info; class info : public std::enable_shared_from_this<info> @@ -169,7 +179,6 @@ namespace elna::boot virtual std::shared_ptr<type_info> is_type(); virtual std::shared_ptr<procedure_info> is_procedure(); - virtual std::shared_ptr<constant_info> is_constant(); virtual std::shared_ptr<variable_info> is_variable(); }; @@ -332,18 +341,6 @@ namespace elna::boot bool is_extern() const; }; - class constant_info : public info - { - public: - using variant = typename - std::variant<std::int32_t, std::uint32_t, double, bool, unsigned char, std::nullptr_t, std::string>; - - const variant symbol; - - explicit constant_info(const variant& symbol); - std::shared_ptr<constant_info> is_constant() override; - }; - /** * Variable symbol information. */ @@ -467,13 +464,46 @@ namespace elna::boot }; /** - * If a type is a declared name for another type, look up recursively - * the declared type. If the given type is not an alias, then this function - * returns its argument. + * Resolves alias chains and type qualifiers until the underlying type is + * reached. If the given type is neither an alias nor qualified, it is + * returned as is. + * + * \param alias The type to resolve. + * \return The underlying, unqualified type. + */ + type resolve_underlying_type(const type& alias); + + /** + * \overload + */ + type resolve_underlying_type(std::shared_ptr<alias_type> alias); + + /** + * Resolves an alias chain until the underlying type or a qualified type is + * reached. + * + * \param checked The type to resolve. + * \return The first non-alias type in the chain. + */ + type resolve_aliases(const type& checked); + + /** + * Checks whether the given type is the built-in type \a name. + * + * \param checked The type to check. + * \param name The name of the primitive type to compare against. + * \return Whether the type is the primitive type with the given name. + */ + bool is_primitive_type(const type& checked, const std::string& name); + + /** + * Checks whether the given type is a pointer type. + * + * A pointer type is considered to be a variable pointer, a procedure + * pointer, or a built-in Pointer. * - * \param alias The type to lookup the innermost declaration for. - * \return Innermost type declaration. + * \param checked The type to check. + * \return Whether the type is a pointer type. */ - type inner_aliased_type(const type& alias); - type inner_aliased_type(std::shared_ptr<alias_type> alias); + bool is_any_pointer_type(const type& checked); } diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index e0f20f3..a1689be 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -76,7 +76,6 @@ namespace elna::gcc void visit(boot::literal<std::string> *string) override; void visit(boot::binary_expression *expression) override; void visit(boot::unary_expression *expression) override; - void visit(boot::constant_declaration *declaration) override; void visit(boot::variable_declaration *declaration) override; void visit(boot::named_expression *expression) override; void visit(boot::array_access_expression *expression) override; diff --git a/source/lexer.elna b/source/lexer.elna index 63ca3d3..c221f07 100644 --- a/source/lexer.elna +++ b/source/lexer.elna @@ -4,9 +4,6 @@ import cstdio, common, cstdlib -const - CHUNK_SIZE := 85536u - type ElnaLexerState = ( start, diff --git a/testsuite/compilable/float_literals.elna b/testsuite/compilable/float_literals.elna index ed23989..77af6b1 100644 --- a/testsuite/compilable/float_literals.elna +++ b/testsuite/compilable/float_literals.elna @@ -1,9 +1,9 @@ proc f() -const - x := 3.14 - y := 1e10 - z := 4.567e8 - t := 2.5E-3 +var + x: const Float := 3.14 + y: const Float := 1e10 + z: const Float := 4.567e8 + t: const Float := 2.5E-3 return end. diff --git a/testsuite/compilable/pointer_const_conversion.elna b/testsuite/compilable/pointer_const_conversion.elna new file mode 100644 index 0000000..fd8e097 --- /dev/null +++ b/testsuite/compilable/pointer_const_conversion.elna @@ -0,0 +1,7 @@ +var + x: Int + p: ^const Int + +begin + p := @x +end. diff --git a/testsuite/compilable/take_const_address.elna b/testsuite/compilable/take_const_address.elna new file mode 100644 index 0000000..54a2992 --- /dev/null +++ b/testsuite/compilable/take_const_address.elna @@ -0,0 +1,7 @@ +var + x: ^const Int + y: const Int + +begin + x := @y +end. diff --git a/testsuite/fail_compilation/assign_const_array_pointer.elna b/testsuite/fail_compilation/assign_const_array_pointer.elna new file mode 100644 index 0000000..32dc66d --- /dev/null +++ b/testsuite/fail_compilation/assign_const_array_pointer.elna @@ -0,0 +1,9 @@ +proc f() +var + p: ^[2]Int + q: ^const [2]Int +begin + p := q (* @Error Expected type '\^\[2\]Int', but got '\^const \[2\]Int' *) +return + +end. diff --git a/testsuite/fail_compilation/assign_const_member.elna b/testsuite/fail_compilation/assign_const_member.elna new file mode 100644 index 0000000..8aecb01 --- /dev/null +++ b/testsuite/fail_compilation/assign_const_member.elna @@ -0,0 +1,14 @@ +type + R = record + x: const Int + end + +proc f() +var + r1: R := R{ x: 5 } + r2: R := R{ x: 5 } +begin + r1 := r2 (* @Error Cannot assign to a value of type 'R', because it is constant or contains constant members *) +return + +end. diff --git a/testsuite/fail_compilation/assign_const_primitive.elna b/testsuite/fail_compilation/assign_const_primitive.elna new file mode 100644 index 0000000..34b095b --- /dev/null +++ b/testsuite/fail_compilation/assign_const_primitive.elna @@ -0,0 +1,8 @@ +proc f() +var + x: const Int +begin + x := 5 (* @Error Cannot assign to a value of type 'const Int', because it is constant or contains constant members *) +return + +end. diff --git a/testsuite/fail_compilation/assign_const_record_pointer.elna b/testsuite/fail_compilation/assign_const_record_pointer.elna new file mode 100644 index 0000000..5e6f3df --- /dev/null +++ b/testsuite/fail_compilation/assign_const_record_pointer.elna @@ -0,0 +1,17 @@ +type + B = record + x: Int + end + D = record(B) + y: Int + end + +proc f() +var + b: ^B + d: ^const D +begin + b := d (* @Error Expected type '\^B', but got '\^const D' *) +return + +end. diff --git a/testsuite/fail_compilation/assign_deep_const_pointer.elna b/testsuite/fail_compilation/assign_deep_const_pointer.elna new file mode 100644 index 0000000..7ed3840 --- /dev/null +++ b/testsuite/fail_compilation/assign_deep_const_pointer.elna @@ -0,0 +1,9 @@ +proc f() +var + p: ^^const Int + q: ^^Int +begin + p := q (* @Error Expected type '\^\^const Int', but got '\^\^Int' *) +return + +end. diff --git a/testsuite/fail_compilation/assign_element_of_const.elna b/testsuite/fail_compilation/assign_element_of_const.elna new file mode 100644 index 0000000..9afd520 --- /dev/null +++ b/testsuite/fail_compilation/assign_element_of_const.elna @@ -0,0 +1,8 @@ +proc f() +var + a: const [2]Int +begin + a[1] := 6 (* @Error Cannot assign to a value of type 'const Int', because it is constant or contains constant members *) +return + +end. diff --git a/testsuite/fail_compilation/assign_member_of_const.elna b/testsuite/fail_compilation/assign_member_of_const.elna new file mode 100644 index 0000000..fc945bb --- /dev/null +++ b/testsuite/fail_compilation/assign_member_of_const.elna @@ -0,0 +1,13 @@ +type + R = record + x: Int + end + +proc f() +var + r: const R := R{ x: 5 } +begin + r.x := 6 (* @Error Cannot assign to a value of type 'const Int', because it is constant or contains constant members *) +return + +end. diff --git a/testsuite/fail_compilation/local_const_exported.elna b/testsuite/fail_compilation/local_const_exported.elna deleted file mode 100644 index 1c6c728..0000000 --- a/testsuite/fail_compilation/local_const_exported.elna +++ /dev/null @@ -1,6 +0,0 @@ -proc test_local_export() -const - c* := 42 (* @Error Local symbol 'c' cannot be exported *) -return - -end. diff --git a/testsuite/runnable/const_copy.elna b/testsuite/runnable/const_copy.elna new file mode 100644 index 0000000..5982149 --- /dev/null +++ b/testsuite/runnable/const_copy.elna @@ -0,0 +1,12 @@ +proc f() +var + x: const Int := 5 + y: Int +begin + y := x; + assert(y = 5) +return + +begin + f() +end. diff --git a/testsuite/runnable/const_initialization.elna b/testsuite/runnable/const_initialization.elna new file mode 100644 index 0000000..7f922ff --- /dev/null +++ b/testsuite/runnable/const_initialization.elna @@ -0,0 +1,10 @@ +proc f() +var + x: const Int := 5 +begin + assert(x = 5) +return + +begin + f() +end. |
