aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/elna/boot/ast.h21
-rw-r--r--include/elna/boot/dependency.h2
-rw-r--r--include/elna/boot/evaluator.h137
-rw-r--r--include/elna/boot/result.h103
-rw-r--r--include/elna/boot/symbol.h8
-rw-r--r--include/elna/boot/type_check.h31
-rw-r--r--include/elna/gcc/elna-generic.h5
7 files changed, 192 insertions, 115 deletions
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index d6804df..8ee0e8b 100644
--- a/include/elna/boot/ast.h
+++ b/include/elna/boot/ast.h
@@ -407,6 +407,7 @@ namespace elna::boot
const std::string& name() const;
const identifier& id() const;
expression& value() const;
+ void value(expression& value);
private:
identifier m_name;
@@ -417,7 +418,7 @@ namespace elna::boot
{
public:
const identifier type_name;
- const std::vector<field_initializer> field_initializers;
+ std::vector<field_initializer> field_initializers;
record_constructor_expression(const source_position position,
identifier&& type_name,
@@ -431,7 +432,7 @@ namespace elna::boot
public:
const std::uint32_t size;
type_expression *const m_element_type;
- const std::vector<expression *> elements;
+ std::vector<expression *> elements;
array_constructor_expression(const source_position position,
std::uint32_t size, type_expression *element_type,
@@ -478,7 +479,7 @@ namespace elna::boot
const std::vector<identifier_definition> identifiers;
type_expression& variable_type();
- expression *const initializer{ nullptr };
+ expression *initializer{ nullptr };
const bool is_extern{ false };
};
@@ -579,7 +580,8 @@ namespace elna::boot
cast_expression *is_cast() override;
type_expression& target();
- expression& value();
+ expression& value() const;
+ void value(expression& value);
~cast_expression() override;
};
@@ -744,7 +746,7 @@ namespace elna::boot
designator_expression *m_callable;
public:
- const std::vector<expression *> arguments;
+ std::vector<expression *> arguments;
procedure_call(const source_position position, designator_expression *callable,
std::vector<expression *>&& arguments);
@@ -941,8 +943,10 @@ namespace elna::boot
void accept(parser_visitor *visitor) override;
binary_expression *is_binary() override;
- expression& lhs();
- expression& rhs();
+ expression& lhs() const;
+ void lhs(expression& lhs);
+ expression& rhs() const;
+ void rhs(expression& rhs);
binary_operator operation() const;
void operation(binary_operator operation);
@@ -961,7 +965,8 @@ namespace elna::boot
void accept(parser_visitor *visitor) override;
unary_expression *is_unary() override;
- expression& operand();
+ expression& operand() const;
+ void operand(expression& operand);
unary_operator operation() const;
void operation(unary_operator operation);
diff --git a/include/elna/boot/dependency.h b/include/elna/boot/dependency.h
index e6cfb36..6a3a412 100644
--- a/include/elna/boot/dependency.h
+++ b/include/elna/boot/dependency.h
@@ -39,7 +39,7 @@ namespace elna::boot
dependency read_source(std::istream& entry_point);
std::filesystem::path build_path(const std::vector<std::string>& segments);
- error_list analyze_semantics(std::unique_ptr<unit>& tree, const symbol_bag& bag,
+ error_list analyze_semantics(std::unique_ptr<unit>& tree, symbol_bag& bag,
const target_info& target);
template<typename T>
diff --git a/include/elna/boot/evaluator.h b/include/elna/boot/evaluator.h
index 2d832bf..5812ee8 100644
--- a/include/elna/boot/evaluator.h
+++ b/include/elna/boot/evaluator.h
@@ -18,7 +18,6 @@ along with GCC; see the file COPYING3. If not see
#pragma once
#include <cstdint>
-#include <map>
#include <memory>
#include <optional>
#include <string>
@@ -30,93 +29,14 @@ along with GCC; see the file COPYING3. If not see
namespace elna::boot
{
/**
- * Size and alignment of a single type on the target machine.
+ * A module-level variable initializer must be a constant expression.
*/
- struct type_properties
+ class non_constant_initializer_error : public error
{
- std::size_t size{0};
- std::size_t alignment{0};
- };
-
- /**
- * Size, alignment and offset of each field of a record.
- */
- struct record_properties
- {
- ordered_map<std::size_t> offset_map;
- std::size_t size;
- std::size_t alignment;
- };
-
- /**
- * Target machine information, populated by the compiler backend glue layer.
- */
- struct target_info
- {
- type_properties int_properties;
- type_properties word_properties;
- type_properties pointer_properties;
- type_properties char_properties;
- type_properties float_properties;
- type_properties bool_properties;
- };
-
- template<template<typename, typename> typename C, template<typename> typename Alloc = std::allocator>
- class constant_aggregate;
-
- /**
- * Typed constant value produced by the constant expression evaluator.
- *
- * Record constructors are stored as ordered_map keyed by field name,
- * array constructors as a vector of elements.
- */
- using constant_value = std::variant<
- std::int32_t,
- std::uint32_t,
- double,
- bool,
- unsigned char,
- std::nullptr_t,
- constant_aggregate<ordered_map>,
- constant_aggregate<std::vector>
- >;
-
- template<template<typename, typename> typename C, template<typename> typename Alloc>
- class constant_aggregate
- {
- using Container = C<constant_value, Alloc<constant_value>>;
- std::shared_ptr<Container> container;
-
public:
- explicit constant_aggregate(const Container& value)
- : container(std::make_shared<Container>(value))
- {
- }
-
- explicit constant_aggregate(const Container&& value)
- : container(std::make_shared<Container>(std::move(value)))
- {
- }
-
- Container& operator*()
- {
- return *this->container;
- }
-
- const Container& operator*() const
- {
- return *this->container;
- }
-
- Container *operator->()
- {
- return this->container.get();
- }
-
- const Container *operator->() const
- {
- return this->container.get();
- }
+ explicit non_constant_initializer_error(const source_position position);
+
+ std::string what() const override;
};
std::optional<type_properties> get_type_properties(const type& subject, const target_info& target);
@@ -142,20 +62,22 @@ namespace elna::boot
{
symbol_bag& bag;
const target_info& target;
- const std::map<std::string, expression*>& evaluated_initializers;
static std::optional<constant_value> evaluate_literal(literal_expression& subject);
std::optional<constant_value> evaluate_named(named_expression& subject);
+ std::optional<constant_value> evaluate_array_access(array_access_expression& subject);
+ std::optional<constant_value> evaluate_field_access(field_access_expression& subject);
+ std::optional<constant_value> evaluate_slicing(slicing_expression& subject);
std::optional<constant_value> evaluate_unary(unary_expression& subject);
std::optional<constant_value> evaluate_binary(binary_expression& subject);
std::optional<constant_value> evaluate_cast(cast_expression& subject);
std::optional<std::size_t> evaluate_traits_size(const type& subject);
std::optional<std::size_t> evaluate_traits_alignment(const type& subject);
+ void fold_aggregate_field(field_initializer& field_init);
public:
std::optional<constant_value> evaluate_traits(traits_expression& subject);
- explicit evaluator(symbol_bag& bag, const target_info& target,
- const std::map<std::string, expression*>& evaluated_initializers);
+ explicit evaluator(symbol_bag& bag, const target_info& target);
/**
* Evaluates an expression at compile time.
@@ -165,5 +87,44 @@ namespace elna::boot
* expression is not constant.
*/
std::optional<constant_value> evaluate(expression& subject);
+
+ /**
+ * Folds an expression into literals.
+ *
+ * For scalars, the original expression is deleted and a new
+ * literal node is returned. For constructors, fields or
+ * elements are folded recursively and the original node is
+ * returned.
+ *
+ * \param original Expression to fold.
+ * \return The folded expression (either a new literal or the
+ * original aggregate with folded children).
+ */
+ expression *fold(expression& original);
+ };
+
+ /**
+ * Folding pass that validates constant variable initializers.
+ *
+ * Runs after name analysis and type checking. Walks all variable
+ * declarations, evaluates constant initializers, and records them
+ * so later declarations can chain through earlier ones.
+ */
+ class constant_folder final : public walking_visitor, public error_container
+ {
+ symbol_bag& bag;
+ const target_info& target;
+ evaluator constant_evaluator;
+
+ expression& fold_trait(expression& expr);
+
+ public:
+ constant_folder(symbol_bag& bag, const target_info& target);
+
+ void visit(variable_declaration* declaration) override;
+ void visit(cast_expression *expr) override;
+ void visit(binary_expression *expr) override;
+ void visit(unary_expression *expr) override;
+ void visit(procedure_call *call) override;
};
}
diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h
index 0719151..89753c1 100644
--- a/include/elna/boot/result.h
+++ b/include/elna/boot/result.h
@@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see
#pragma once
#include <cstddef>
+#include <cstdint>
#include <string>
#include <deque>
#include <memory>
@@ -304,6 +305,108 @@ namespace elna::boot
return this->payload.cend();
}
};
+
+ /**
+ * Size and alignment of a single type on the target machine.
+ */
+ struct type_properties
+ {
+ std::size_t size{0};
+ std::size_t alignment{0};
+ };
+
+ /**
+ * Size, alignment and offset of each field of a record.
+ */
+ struct record_properties
+ {
+ ordered_map<std::size_t> offset_map;
+ std::size_t size;
+ std::size_t alignment;
+ };
+
+ /**
+ * Target machine information, populated by the compiler backend glue layer.
+ */
+ struct target_info
+ {
+ type_properties int_properties;
+ type_properties word_properties;
+ type_properties pointer_properties;
+ type_properties char_properties;
+ type_properties float_properties;
+ type_properties bool_properties;
+ };
+
+ /**
+ * Address of a module-level variable, tracked by name so that
+ * different variables have distinct addresses at compile time.
+ */
+ struct global_address
+ {
+ std::string name;
+
+ auto operator<=>(const global_address&) const = default;
+ };
+
+ template<template<typename, typename> typename C, template<typename> typename Alloc = std::allocator>
+ class constant_aggregate;
+
+ /**
+ * Typed constant value produced by the constant expression evaluator.
+ *
+ * Record constructors are stored as ordered_map keyed by field name,
+ * array constructors as a vector of elements.
+ */
+ using constant_value = std::variant<
+ std::int32_t,
+ std::uint32_t,
+ double,
+ bool,
+ unsigned char,
+ std::nullptr_t,
+ global_address,
+ constant_aggregate<ordered_map>,
+ constant_aggregate<std::vector>
+ >;
+
+ template<template<typename, typename> typename C, template<typename> typename Alloc>
+ class constant_aggregate
+ {
+ using Container = C<constant_value, Alloc<constant_value>>;
+ std::shared_ptr<Container> container;
+
+ public:
+ explicit constant_aggregate(const Container& value)
+ : container(std::make_shared<Container>(value))
+ {
+ }
+
+ explicit constant_aggregate(const Container&& value)
+ : container(std::make_shared<Container>(std::move(value)))
+ {
+ }
+
+ Container& operator*()
+ {
+ return *this->container;
+ }
+
+ const Container& operator*() const
+ {
+ return *this->container;
+ }
+
+ Container *operator->()
+ {
+ return this->container.get();
+ }
+
+ const Container *operator->() const
+ {
+ return this->container.get();
+ }
+ };
}
template<>
diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h
index 5b56904..46d9c2c 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -20,13 +20,14 @@ along with GCC; see the file COPYING3. If not see
#include <cstdint>
#include <forward_list>
#include <memory>
+#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
-#include <variant>
-#include <vector>
#include "elna/boot/result.h"
+#include <variant>
+#include <vector>
namespace elna::boot
{
@@ -359,6 +360,9 @@ namespace elna::boot
/// Whether this is an extern symbol.
const bool is_extern;
+ /// Evaluated constant value, set by the constant folder.
+ std::optional<constant_value> value;
+
/**
* Constructs a variable symbol information.
*
diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h
index 3a2b61d..bd32c08 100644
--- a/include/elna/boot/type_check.h
+++ b/include/elna/boot/type_check.h
@@ -22,7 +22,6 @@ along with GCC; see the file COPYING3. If not see
#include <vector>
#include "elna/boot/ast.h"
-#include "elna/boot/evaluator.h"
#include "elna/boot/result.h"
#include "elna/boot/symbol.h"
@@ -125,18 +124,30 @@ namespace elna::boot
};
/**
- * Type passed to a trait like #min or #max does not support
- * the trait.
+ * A trait invocation has invalid arguments.
*/
- /**
- * A module-level variable initializer must be a constant expression.
- */
- class non_constant_initializer_error : public error
+ class trait_error : public error
{
public:
- explicit non_constant_initializer_error(const source_position position);
+ /// Wrong number of arguments passed to a trait.
+ struct argument_count
+ {
+ std::size_t expected;
+ std::size_t actual;
+ };
+ /// \c \#offset second argument is not a field name.
+ struct offset_not_field_name {};
+
+ using payload_type = std::variant<argument_count, offset_not_field_name>;
+
+ trait_error(const source_position position, const std::string& trait_name,
+ payload_type payload);
std::string what() const override;
+
+ private:
+ std::string trait_name;
+ payload_type m_payload;
};
/**
@@ -217,9 +228,6 @@ namespace elna::boot
std::shared_ptr<procedure_info> current_procedure;
const target_info& target;
- // Map from const variable name to its initializer, for chaining.
- std::map<std::string, expression*> evaluated_initializers;
-
/*
* Whether an expression of type assignment can be assigned to a variable
* of type assignee.
@@ -248,6 +256,7 @@ namespace elna::boot
void visit(if_statement *statement) override;
void visit(record_constructor_expression *expression) override;
void visit(array_constructor_expression *expression) override;
+ void visit(traits_expression *trait) override;
void visit(slicing_expression *expression) override;
void visit(unary_expression *expression) override;
void visit(binary_expression *expression) override;
diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h
index 27fdb43..c2f154f 100644
--- a/include/elna/gcc/elna-generic.h
+++ b/include/elna/gcc/elna-generic.h
@@ -17,11 +17,9 @@ along with GCC; see the file COPYING3. If not see
#pragma once
-#include <map>
#include <memory>
#include <string>
#include "elna/boot/ast.h"
-#include "elna/boot/evaluator.h"
#include "elna/boot/symbol.h"
#include "elna/gcc/elna-tree.h"
@@ -39,8 +37,6 @@ namespace elna::gcc
elna::boot::symbol_bag bag;
std::shared_ptr<symbol_table> symbols;
const elna::boot::target_info& target;
- std::map<std::string, boot::expression *> evaluated_initializers;
- std::unique_ptr<boot::evaluator> const_evaluator;
static tree build_equality(boot::binary_expression *expression, tree left, tree right);
static tree build_equality_comparison(location_t loc, tree left, tree right,
@@ -76,7 +72,6 @@ namespace elna::gcc
void visit(boot::procedure_declaration *declaration) override;
void visit(boot::procedure_call *call) override;
void visit(boot::cast_expression *expression) override;
- void visit(boot::traits_expression *trait) override;
void visit(boot::literal<std::int32_t> *literal) override;
void visit(boot::literal<std::uint32_t> *literal) override;
void visit(boot::literal<double> *literal) override;