Type check pointer dereferencing
This commit is contained in:
parent
68264016ce
commit
9f2c5ad9e3
1
TODO
1
TODO
@ -12,6 +12,7 @@
|
|||||||
- Syscalls.
|
- Syscalls.
|
||||||
- Error message with an empty file wrongly says that a ")" is expected.
|
- Error message with an empty file wrongly says that a ")" is expected.
|
||||||
- Support any expressions for constants.
|
- Support any expressions for constants.
|
||||||
|
- Name analysis should fail if there are undefined symbols.
|
||||||
|
|
||||||
# Toolchain
|
# Toolchain
|
||||||
- Try to guess the build platform (x86_64-slackware-linux is hard coded).
|
- Try to guess the build platform (x86_64-slackware-linux is hard coded).
|
||||||
|
@ -53,8 +53,8 @@ namespace elna::cli
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
auto global_scope = add_builtin_symbols();
|
auto global_scope = add_builtin_symbols();
|
||||||
source::name_analysis_visitor(global_scope).visit(ast.get());
|
source::name_analysis_visitor(global_scope, in_file).visit(ast.get());
|
||||||
source::type_analysis_visitor().visit(ast.get());
|
source::type_analysis_visitor(global_scope, in_file, 4).visit(ast.get());
|
||||||
source::allocator_visitor(global_scope).visit(ast.get());
|
source::allocator_visitor(global_scope).visit(ast.get());
|
||||||
|
|
||||||
source::intermediate_code_generator intermediate_code_generator{ global_scope };
|
source::intermediate_code_generator intermediate_code_generator{ global_scope };
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <boost/core/noncopyable.hpp>
|
#include <boost/core/noncopyable.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <elna/source/lexer.hpp>
|
#include <elna/source/lexer.hpp>
|
||||||
|
#include "elna/source/types.hpp"
|
||||||
|
|
||||||
namespace elna::source
|
namespace elna::source
|
||||||
{
|
{
|
||||||
@ -173,6 +174,7 @@ namespace elna::source
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<operand> place;
|
std::shared_ptr<operand> place;
|
||||||
|
std::shared_ptr<const type> data_type;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include "elna/source/types.hpp"
|
||||||
|
|
||||||
namespace elna::source
|
namespace elna::source
|
||||||
{
|
{
|
||||||
@ -123,6 +124,32 @@ namespace elna::source
|
|||||||
std::string what() const override;
|
std::string what() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct type_mismatch final : public error
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Kind of the operation on the type.
|
||||||
|
*/
|
||||||
|
enum class operation
|
||||||
|
{
|
||||||
|
dereference,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \param name Given type.
|
||||||
|
* \param kind Kind of the operation on the type.
|
||||||
|
* \param path Source file name.
|
||||||
|
* \param position Operation position.
|
||||||
|
*/
|
||||||
|
type_mismatch(std::shared_ptr<const type> got, operation kind, const std::filesystem::path& path,
|
||||||
|
const struct position position);
|
||||||
|
|
||||||
|
std::string what() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<const type> got;
|
||||||
|
operation kind;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct writer
|
struct writer
|
||||||
{
|
{
|
||||||
|
@ -7,12 +7,23 @@ namespace elna::source
|
|||||||
{
|
{
|
||||||
class name_analysis_visitor final : public empty_visitor
|
class name_analysis_visitor final : public empty_visitor
|
||||||
{
|
{
|
||||||
std::shared_ptr<symbol_table> table = std::make_shared<symbol_table>();
|
std::shared_ptr<symbol_table> table;
|
||||||
|
const std::filesystem::path filename;
|
||||||
|
std::list<std::unique_ptr<error>> m_errors;
|
||||||
|
|
||||||
std::shared_ptr<const type> convert_declaration_type(const type_expression& ast_type) const;
|
std::shared_ptr<const type> convert_declaration_type(const type_expression& ast_type) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
name_analysis_visitor(std::shared_ptr<symbol_table> table);
|
/**
|
||||||
|
* \param table Symbol table.
|
||||||
|
* \param path Source filename.
|
||||||
|
*/
|
||||||
|
name_analysis_visitor(std::shared_ptr<symbol_table> table, const std::filesystem::path& filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return Collected errors.
|
||||||
|
*/
|
||||||
|
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
||||||
|
|
||||||
void visit(constant_definition *definition) override;
|
void visit(constant_definition *definition) override;
|
||||||
void visit(declaration *declaration) override;
|
void visit(declaration *declaration) override;
|
||||||
@ -44,5 +55,29 @@ namespace elna::source
|
|||||||
*/
|
*/
|
||||||
class type_analysis_visitor final : public empty_visitor
|
class type_analysis_visitor final : public empty_visitor
|
||||||
{
|
{
|
||||||
|
std::shared_ptr<symbol_table> table;
|
||||||
|
const std::filesystem::path filename;
|
||||||
|
const std::size_t pointer_size;
|
||||||
|
std::list<std::unique_ptr<error>> m_errors;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \param table Symbol table.
|
||||||
|
* \param path Source filename.
|
||||||
|
* \param target_pointer_size Pointer size on the target platform.
|
||||||
|
*/
|
||||||
|
type_analysis_visitor(std::shared_ptr<symbol_table> table, const std::filesystem::path& filename,
|
||||||
|
std::size_t target_pointer_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return Collected errors.
|
||||||
|
*/
|
||||||
|
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
||||||
|
|
||||||
|
void visit(program *program) override;
|
||||||
|
void visit(procedure_definition *definition) override;
|
||||||
|
void visit(integer_literal *literal) override;
|
||||||
|
void visit(boolean_literal *literal) override;
|
||||||
|
void visit(unary_expression *expression) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,23 @@ namespace elna::source
|
|||||||
/**
|
/**
|
||||||
* Type representation.
|
* Type representation.
|
||||||
*/
|
*/
|
||||||
struct type
|
class type
|
||||||
{
|
{
|
||||||
const std::size_t byte_size;
|
const std::size_t byte_size;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* \param byte_size The type size in bytes.
|
||||||
|
*/
|
||||||
explicit type(const std::size_t byte_size);
|
explicit type(const std::size_t byte_size);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return The type size in bytes.
|
||||||
|
*/
|
||||||
|
virtual std::size_t size() const noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,8 +32,15 @@ namespace elna::source
|
|||||||
*/
|
*/
|
||||||
struct primitive_type : public type
|
struct primitive_type : public type
|
||||||
{
|
{
|
||||||
|
/// Type name.
|
||||||
const std::string type_name;
|
const std::string type_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* \param type_name Type name.
|
||||||
|
* \param byte_size The type size in bytes.
|
||||||
|
*/
|
||||||
primitive_type(const std::string& type_name, const std::size_t byte_size);
|
primitive_type(const std::string& type_name, const std::size_t byte_size);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,8 +49,15 @@ namespace elna::source
|
|||||||
*/
|
*/
|
||||||
struct pointer_type : public type
|
struct pointer_type : public type
|
||||||
{
|
{
|
||||||
|
/// Pointer target type.
|
||||||
std::shared_ptr<const type> base_type;
|
std::shared_ptr<const type> base_type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* \param base_type Pointer target type.
|
||||||
|
* \param byte_size The type size in bytes.
|
||||||
|
*/
|
||||||
pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size);
|
pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include "elna/source/result.hpp"
|
#include "elna/source/result.hpp"
|
||||||
#include "elna/source/types.hpp"
|
|
||||||
|
|
||||||
namespace elna::source
|
namespace elna::source
|
||||||
{
|
{
|
||||||
@ -33,4 +32,15 @@ namespace elna::source
|
|||||||
{
|
{
|
||||||
return "Name '" + name + "' was already defined";
|
return "Name '" + name + "' was already defined";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type_mismatch::type_mismatch(std::shared_ptr<const type> got, operation kind, const std::filesystem::path& path,
|
||||||
|
const struct position position)
|
||||||
|
: error(path, position), kind(kind), got(got)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string type_mismatch::what() const
|
||||||
|
{
|
||||||
|
return "Type cannot be used here.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
#include "elna/source/semantic.hpp"
|
#include "elna/source/semantic.hpp"
|
||||||
#include "elna/source/types.hpp"
|
#include "elna/source/result.hpp"
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
namespace elna::source
|
namespace elna::source
|
||||||
{
|
{
|
||||||
name_analysis_visitor::name_analysis_visitor(std::shared_ptr<symbol_table> table)
|
name_analysis_visitor::name_analysis_visitor(std::shared_ptr<symbol_table> table,
|
||||||
: table(table)
|
const std::filesystem::path& filename)
|
||||||
|
: table(table), filename(filename)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +64,11 @@ namespace elna::source
|
|||||||
this->table = info->scope()->scope();
|
this->table = info->scope()->scope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::list<std::unique_ptr<error>>& name_analysis_visitor::errors() const noexcept
|
||||||
|
{
|
||||||
|
return m_errors;
|
||||||
|
}
|
||||||
|
|
||||||
allocator_visitor::allocator_visitor(std::shared_ptr<symbol_table> table)
|
allocator_visitor::allocator_visitor(std::shared_ptr<symbol_table> table)
|
||||||
: table(table)
|
: table(table)
|
||||||
{
|
{
|
||||||
@ -115,4 +121,69 @@ namespace elna::source
|
|||||||
this->argument_offset = std::max(static_cast<std::size_t>(this->argument_offset),
|
this->argument_offset = std::max(static_cast<std::size_t>(this->argument_offset),
|
||||||
call_info->parameter_stack_size());
|
call_info->parameter_stack_size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type_analysis_visitor::type_analysis_visitor(std::shared_ptr<symbol_table> table,
|
||||||
|
const std::filesystem::path& filename, std::size_t target_pointer_size)
|
||||||
|
: table(table), filename(filename), pointer_size(target_pointer_size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void type_analysis_visitor::visit(program *program)
|
||||||
|
{
|
||||||
|
for (auto& definition : program->definitions())
|
||||||
|
{
|
||||||
|
if (dynamic_cast<procedure_definition *>(definition.get()) != nullptr)
|
||||||
|
{
|
||||||
|
definition->accept(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
program->body().accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void type_analysis_visitor::visit(procedure_definition *definition)
|
||||||
|
{
|
||||||
|
definition->body().accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void type_analysis_visitor::visit(integer_literal *literal)
|
||||||
|
{
|
||||||
|
literal->data_type = std::dynamic_pointer_cast<type_info>(table->lookup("Int"))->type();
|
||||||
|
}
|
||||||
|
|
||||||
|
void type_analysis_visitor::visit(boolean_literal *literal)
|
||||||
|
{
|
||||||
|
literal->data_type = std::dynamic_pointer_cast<type_info>(table->lookup("Boolean"))->type();
|
||||||
|
}
|
||||||
|
|
||||||
|
void type_analysis_visitor::visit(unary_expression *expression)
|
||||||
|
{
|
||||||
|
empty_visitor::visit(expression);
|
||||||
|
|
||||||
|
switch (expression->operation())
|
||||||
|
{
|
||||||
|
case unary_operator::reference:
|
||||||
|
expression->data_type = std::make_shared<const pointer_type>(expression->operand().data_type,
|
||||||
|
this->pointer_size);
|
||||||
|
break;
|
||||||
|
case unary_operator::dereference:
|
||||||
|
auto operand_type = expression->operand().data_type;
|
||||||
|
|
||||||
|
if (auto referenced_type = std::dynamic_pointer_cast<const pointer_type>(operand_type))
|
||||||
|
{
|
||||||
|
expression->data_type = referenced_type;
|
||||||
|
}
|
||||||
|
else if (operand_type != nullptr)
|
||||||
|
{
|
||||||
|
auto new_error = std::make_unique<type_mismatch>(operand_type,
|
||||||
|
type_mismatch::operation::dereference, this->filename, expression->position());
|
||||||
|
m_errors.push_back(std::move(new_error));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::list<std::unique_ptr<error>>& type_analysis_visitor::errors() const noexcept
|
||||||
|
{
|
||||||
|
return m_errors;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,11 @@ namespace elna::source
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t type::size() const noexcept
|
||||||
|
{
|
||||||
|
return this->byte_size;
|
||||||
|
}
|
||||||
|
|
||||||
primitive_type::primitive_type(const std::string& type_name, const std::size_t byte_size)
|
primitive_type::primitive_type(const std::string& type_name, const std::size_t byte_size)
|
||||||
: type(byte_size), type_name(type_name)
|
: type(byte_size), type_name(type_name)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user