Modules wip: extract AST preprocessing to a separate 'semantic' library, move fake root AST node for module entry point to IR compiler internals

This commit is contained in:
Nikita Lisitsa 2026-08-02 22:00:02 +03:00
parent b16894ebd1
commit cb84430883
21 changed files with 366 additions and 326 deletions

View file

@ -6,6 +6,7 @@ set(CMAKE_CXX_STANDARD 23)
add_subdirectory(libs/types) add_subdirectory(libs/types)
add_subdirectory(libs/ast) add_subdirectory(libs/ast)
add_subdirectory(libs/parser) add_subdirectory(libs/parser)
add_subdirectory(libs/semantic)
add_subdirectory(libs/ir) add_subdirectory(libs/ir)
add_subdirectory(libs/jit) add_subdirectory(libs/jit)
add_subdirectory(libs/interpreter) add_subdirectory(libs/interpreter)

View file

@ -3,8 +3,9 @@
#include <pslang/interpreter/exec.hpp> #include <pslang/interpreter/exec.hpp>
#include <pslang/interpreter/error.hpp> #include <pslang/interpreter/error.hpp>
#include <pslang/ast/statement.hpp> #include <pslang/ast/statement.hpp>
#include <pslang/ast/preprocess.hpp>
#include <pslang/ast/print.hpp> #include <pslang/ast/print.hpp>
#include <pslang/semantic/preprocess.hpp>
#include <pslang/semantic/error.hpp>
#include <pslang/ir/compiler.hpp> #include <pslang/ir/compiler.hpp>
#include <pslang/ir/print.hpp> #include <pslang/ir/print.hpp>
#include <pslang/jit/jit.hpp> #include <pslang/jit/jit.hpp>
@ -95,7 +96,7 @@ int main(int argc, char ** argv)
bool jit = false; bool jit = false;
std::vector<std::string> filenames; std::vector<std::string> filenames;
std::vector<ast::statement_ptr> parsed; std::vector<semantic::module> modules;
std::vector<ir::module_context> ir_compiled; std::vector<ir::module_context> ir_compiled;
bool no_more_options = false; bool no_more_options = false;
@ -182,23 +183,23 @@ int main(int argc, char ** argv)
try try
{ {
filenames.push_back(argv[arg]); filenames.push_back(argv[arg]);
auto root = parser::parse(filenames.back()); semantic::module module;
module.statements = parser::parse(filenames.back());
if (dump_ast) if (dump_ast)
{ {
std::cout << "Input file " << filenames.back() << " AST dump:\n\n"; std::cout << "Input file " << filenames.back() << " AST dump:\n\n";
if (auto function_definition = std::get_if<ast::function_definition>(root.get())) ast::print(std::cout, *module.statements);
ast::print(std::cout, *function_definition->statements);
std::cout << "\n" << std::flush; std::cout << "\n" << std::flush;
} }
ast::resolve_identifiers(root); semantic::resolve_identifiers(module);
ast::check_and_infer_types(root); semantic::check_and_infer_types(module);
ast::validate(root); semantic::validate(module);
parsed.push_back(std::move(root)); modules.push_back(std::move(module));
ir_compiled.emplace_back(); ir_compiled.emplace_back();
ir::compile(ir_compiled.back(), parsed.back()); ir::compile(ir_compiled.back(), modules.back().statements);
if (dump_ir) if (dump_ir)
{ {
@ -207,25 +208,25 @@ int main(int argc, char ** argv)
std::cout << "\n" << std::flush; std::cout << "\n" << std::flush;
} }
} }
catch (ast::parse_error const & error) catch (parser::parse_error const & error)
{ {
std::cerr << "Parse error at " << error.location() << ":\n " << error.what() << std::endl; std::cerr << "Parse error at " << error.location() << ":\n " << error.what() << std::endl;
print_error_context(argv[arg], error.location()); print_error_context(argv[arg], error.location());
return EXIT_FAILURE; return EXIT_FAILURE;
} }
catch (ast::type_error const & error) catch (semantic::type_error const & error)
{ {
std::cerr << "Type error at " << error.location() << ":\n " << error.what() << std::endl; std::cerr << "Type error at " << error.location() << ":\n " << error.what() << std::endl;
print_error_context(argv[arg], error.location()); print_error_context(argv[arg], error.location());
return EXIT_FAILURE; return EXIT_FAILURE;
} }
catch (ast::invalid_ast_error const & error) catch (semantic::internal_error const & error)
{ {
std::cerr << "Invalid AST at " << error.location() << ":\n " << error.what() << std::endl; std::cerr << "Internal error at " << error.location() << ":\n " << error.what() << std::endl;
print_error_context(argv[arg], error.location()); print_error_context(argv[arg], error.location());
return EXIT_FAILURE; return EXIT_FAILURE;
} }
catch (ast::validation_error const & error) catch (semantic::validation_error const & error)
{ {
std::cerr << "Validation error at " << error.location() << ":\n " << error.what() << std::endl; std::cerr << "Validation error at " << error.location() << ":\n " << error.what() << std::endl;
print_error_context(argv[arg], error.location()); print_error_context(argv[arg], error.location());

View file

@ -17,23 +17,6 @@ namespace pslang::ast
types::type_ptr inferred_type = nullptr; types::type_ptr inferred_type = nullptr;
}; };
struct if_block
{
expression_ptr condition;
ast::location location;
};
struct else_block
{
ast::location location;
};
struct else_if_block
{
expression_ptr condition;
ast::location location;
};
// Interpreted as a consecutive "if -> else if -> else if -> else" chain // Interpreted as a consecutive "if -> else if -> else if -> else" chain
// Empty condition means no condition (last "else" in chain) // Empty condition means no condition (last "else" in chain)
// All blocks but the last must have a non-empty condition // All blocks but the last must have a non-empty condition

View file

@ -36,28 +36,4 @@ namespace pslang::ast
ast::location location_; ast::location location_;
}; };
struct parse_error
: error
{
using error::error;
};
struct type_error
: error
{
using error::error;
};
struct invalid_ast_error
: error
{
using error::error;
};
struct validation_error
: error
{
using error::error;
};
} }

View file

@ -1,12 +0,0 @@
#pragma once
#include <pslang/ast/statement_fwd.hpp>
namespace pslang::ast
{
void resolve_identifiers(statement_ptr & statements);
void check_and_infer_types(statement_ptr & statements);
void validate(statement_ptr & statements);
}

View file

@ -1,66 +0,0 @@
#include <pslang/ast/preprocess.hpp>
#include <pslang/ast/statement_visitor.hpp>
#include <pslang/ast/error.hpp>
#include <pslang/types/type.hpp>
namespace pslang::ast
{
namespace
{
struct validate_visitor
: const_statement_visitor<validate_visitor>
{
using const_statement_visitor::apply;
void apply(expression_ptr const &) {}
void apply(assignment const &) {}
void apply(variable_declaration const &) {}
void apply(if_chain const & node)
{
for (auto const & block : node.blocks)
apply(*block.statements);
}
void apply(while_block const & node)
{
apply(*node.statements);
}
void apply(break_statement const &)
{}
void apply(continue_statement const &)
{}
void apply(function_definition const & node)
{
apply(*node.statements);
if (!types::equal(*get_type(*node.return_type), types::unit_type{}))
if (node.statements->statements.empty() || (true
&& !std::get_if<return_statement>(node.statements->statements.back().get())
&& !std::get_if<if_chain>(node.statements->statements.back().get())
&& !std::get_if<while_block>(node.statements->statements.back().get())
))
throw validation_error("Function returning non-unit is missing a return statement in the end", node.location);
}
void apply(foreign_function_declaration const &) {}
void apply(return_statement const &) {}
void apply(struct_definition const &) {}
};
}
void validate(statement_ptr & root)
{
validate_visitor{}.apply(*root);
}
}

View file

@ -3,4 +3,4 @@ file(GLOB_RECURSE PSLANG_IR_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
add_library(pslang-ir STATIC ${PSLANG_IR_HEADERS} ${PSLANG_IR_SOURCES}) add_library(pslang-ir STATIC ${PSLANG_IR_HEADERS} ${PSLANG_IR_SOURCES})
target_include_directories(pslang-ir PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") target_include_directories(pslang-ir PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(pslang-ir PUBLIC pslang-types pslang-ast) target_link_libraries(pslang-ir PUBLIC pslang-types pslang-ast pslang-semantic)

View file

@ -31,6 +31,6 @@ namespace pslang::ir
node_ref entry_point; node_ref entry_point;
}; };
void compile(module_context & context, ast::statement_ptr const & root); void compile(module_context & context, ast::statement_list_ptr statements);
} }

View file

@ -911,11 +911,28 @@ namespace pslang::ir
} }
void compile(module_context & mcontext, ast::statement_ptr const & root) void compile(module_context & mcontext, ast::statement_list_ptr statements)
{ {
if (!mcontext.nodes) if (!mcontext.nodes)
mcontext.nodes = std::make_shared<node_list>(); mcontext.nodes = std::make_shared<node_list>();
// Add a fake root AST function node
// for module entry point
auto root = std::make_shared<ast::statement>(
ast::function_definition {
{
"[entry point]",
{},
std::make_shared<ast::type>(types::unit_type{}),
{},
std::make_shared<types::type>(types::unit_type{}),
std::make_shared<types::type>(types::function_type{{}, std::make_shared<types::type>(types::unit_type{})}),
},
statements,
{},
}
);
local_context lcontext; local_context lcontext;
mcontext.nodes->emplace_back(label{}); mcontext.nodes->emplace_back(label{});

View file

@ -9,7 +9,11 @@
namespace pslang::parser namespace pslang::parser
{ {
using parse_error = ast::parse_error; struct parse_error
: ast::error
{
using error::error;
};
struct internal_error struct internal_error
: std::exception : std::exception

View file

@ -5,6 +5,6 @@
namespace pslang::parser namespace pslang::parser
{ {
ast::statement_list_ptr finalize(ast::statement_list_ptr statements); void finalize(ast::statement_list_ptr statements);
} }

View file

@ -7,6 +7,8 @@
namespace pslang::parser namespace pslang::parser
{ {
ast::statement_ptr parse(std::string_view path); // NB: @path should live as long as the resulting AST lives,
// as the latter references the former in node locations
ast::statement_list_ptr parse(std::string_view path);
} }

View file

@ -3,8 +3,6 @@
#include <pslang/ast/statement.hpp> #include <pslang/ast/statement.hpp>
#include <pslang/ast/statement_visitor.hpp> #include <pslang/ast/statement_visitor.hpp>
#include <vector>
namespace pslang::parser namespace pslang::parser
{ {
@ -51,10 +49,9 @@ namespace pslang::parser
} }
ast::statement_list_ptr finalize(ast::statement_list_ptr statements) void finalize(ast::statement_list_ptr statements)
{ {
validate(statements, nullptr, false); validate(statements, nullptr, false);
return statements;
} }
} }

View file

@ -8,7 +8,7 @@
namespace pslang::parser namespace pslang::parser
{ {
ast::statement_ptr parse(std::string_view path) ast::statement_list_ptr parse(std::string_view path)
{ {
yyin = fopen(path.data(), "r"); yyin = fopen(path.data(), "r");
if (!yyin) if (!yyin)
@ -24,17 +24,9 @@ namespace pslang::parser
fclose(yyin); fclose(yyin);
// Add a fake AST node for the entry point finalize(statements);
return std::make_shared<ast::statement>(ast::function_definition{
{ return statements;
"[entry point]",
{},
std::make_shared<ast::type>(types::unit_type{}),
{},
},
finalize(std::move(statements)),
{},
});
} }
} }

View file

@ -0,0 +1,6 @@
file(GLOB_RECURSE PSLANG_SEMANTIC_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp")
file(GLOB_RECURSE PSLANG_SEMANTIC_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
add_library(pslang-semantic STATIC ${PSLANG_SEMANTIC_HEADERS} ${PSLANG_SEMANTIC_SOURCES})
target_include_directories(pslang-semantic PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(pslang-semantic PUBLIC pslang-ast)

View file

@ -0,0 +1,26 @@
#pragma once
#include <pslang/ast/error.hpp>
namespace pslang::semantic
{
struct type_error
: ast::error
{
using error::error;
};
struct validation_error
: ast::error
{
using error::error;
};
struct internal_error
: ast::error
{
using error::error;
};
}

View file

@ -0,0 +1,30 @@
#pragma once
#include <pslang/ast/statement_fwd.hpp>
#include <unordered_map>
namespace pslang::ast
{
struct function_definition;
struct foreign_function_declaration;
struct variable_declaration;
struct struct_definition;
}
namespace pslang::semantic
{
struct module
{
ast::statement_list_ptr statements;
std::unordered_map<std::string, ast::struct_definition *> exported_structs;
std::unordered_map<std::string, ast::function_definition *> exported_functions;
std::unordered_map<std::string, ast::foreign_function_declaration *> exported_foreign_functions;
std::unordered_map<std::string, ast::variable_declaration *> exported_globals;
};
}

View file

@ -0,0 +1,12 @@
#pragma once
#include <pslang/semantic/module.hpp>
namespace pslang::semantic
{
void resolve_identifiers(module & module);
void check_and_infer_types(module & module);
void validate(module & module);
}

View file

@ -1,16 +1,16 @@
#include <pslang/ast/preprocess.hpp> #include <pslang/semantic/preprocess.hpp>
#include <pslang/semantic/error.hpp>
#include <pslang/ast/statement.hpp> #include <pslang/ast/statement.hpp>
#include <pslang/ast/type_visitor.hpp> #include <pslang/ast/type_visitor.hpp>
#include <pslang/ast/expression_visitor.hpp> #include <pslang/ast/expression_visitor.hpp>
#include <pslang/ast/statement_visitor.hpp> #include <pslang/ast/statement_visitor.hpp>
#include <pslang/ast/error.hpp>
#include <pslang/types/type.hpp> #include <pslang/types/type.hpp>
#include <unordered_set> #include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
namespace pslang::ast namespace pslang::semantic
{ {
namespace namespace
@ -18,12 +18,12 @@ namespace pslang::ast
struct scope struct scope
{ {
std::unordered_map<std::string, function_definition *> functions; std::unordered_map<std::string, ast::function_definition *> functions;
std::unordered_map<std::string, foreign_function_declaration *> foreign_functions; std::unordered_map<std::string, ast::foreign_function_declaration *> foreign_functions;
std::unordered_map<std::string, struct_definition *> structs; std::unordered_map<std::string, ast::struct_definition *> structs;
std::unordered_map<std::string, variable_base *> variables; std::unordered_map<std::string, ast::variable_base *> variables;
std::unordered_map<std::string, variable_declaration *> globals; std::unordered_map<std::string, ast::variable_declaration *> globals;
std::unordered_map<std::string, variable_declaration *> constants; std::unordered_map<std::string, ast::variable_declaration *> constants;
bool contains_transitive(std::string const & name) const bool contains_transitive(std::string const & name) const
{ {
@ -66,65 +66,65 @@ namespace pslang::ast
}; };
struct populate_globals_visitor struct populate_globals_visitor
: statement_visitor<populate_globals_visitor> : ast::statement_visitor<populate_globals_visitor>
{ {
std::vector<scope> & scopes; std::vector<scope> & scopes;
using statement_visitor::apply; using statement_visitor::apply;
void apply(expression_ptr const &) void apply(ast::expression_ptr const &)
{} {}
void apply(assignment const &) void apply(ast::assignment const &)
{} {}
void apply(variable_declaration const &) void apply(ast::variable_declaration const &)
{} {}
void apply(if_chain const &) void apply(ast::if_chain const &)
{} {}
void apply(while_block const &) void apply(ast::while_block const &)
{} {}
void apply(break_statement const &) void apply(ast::break_statement const &)
{} {}
void apply(continue_statement const &) void apply(ast::continue_statement const &)
{} {}
void apply(function_definition & function_definition) void apply(ast::function_definition & function_definition)
{ {
if (scopes.back().contains(function_definition.name)) if (scopes.back().contains(function_definition.name))
throw parse_error("Identifier \"" + function_definition.name + "\" is already defined at this scope", function_definition.location); throw validation_error("Identifier \"" + function_definition.name + "\" is already defined at this scope", function_definition.location);
scopes.back().functions[function_definition.name] = &function_definition; scopes.back().functions[function_definition.name] = &function_definition;
} }
void apply(foreign_function_declaration & foreign_function_declaration) void apply(ast::foreign_function_declaration & foreign_function_declaration)
{ {
if (scopes.back().contains(foreign_function_declaration.name)) if (scopes.back().contains(foreign_function_declaration.name))
throw parse_error("Identifier \"" + foreign_function_declaration.name + "\" is already defined at this scope", foreign_function_declaration.location); throw validation_error("Identifier \"" + foreign_function_declaration.name + "\" is already defined at this scope", foreign_function_declaration.location);
scopes.back().foreign_functions[foreign_function_declaration.name] = &foreign_function_declaration; scopes.back().foreign_functions[foreign_function_declaration.name] = &foreign_function_declaration;
} }
void apply(return_statement const &) void apply(ast::return_statement const &)
{} {}
void apply(struct_definition & struct_definition) void apply(ast::struct_definition & struct_definition)
{ {
if (scopes.back().contains(struct_definition.name)) if (scopes.back().contains(struct_definition.name))
throw parse_error("Identifier \"" + struct_definition.name + "\" is already defined at this scope", struct_definition.location); throw validation_error("Identifier \"" + struct_definition.name + "\" is already defined at this scope", struct_definition.location);
scopes.back().structs[struct_definition.name] = &struct_definition; scopes.back().structs[struct_definition.name] = &struct_definition;
} }
}; };
struct resolve_identifiers_visitor struct resolve_identifiers_visitor
: type_visitor<resolve_identifiers_visitor> : ast::type_visitor<resolve_identifiers_visitor>
, expression_visitor<resolve_identifiers_visitor> , ast::expression_visitor<resolve_identifiers_visitor>
, statement_visitor<resolve_identifiers_visitor> , ast::statement_visitor<resolve_identifiers_visitor>
{ {
std::vector<scope> & scopes; std::vector<scope> & scopes;
@ -138,24 +138,24 @@ namespace pslang::ast
void apply(types::primitive_type const &) void apply(types::primitive_type const &)
{} {}
void apply(array_type const & array_type) void apply(ast::array_type const & array_type)
{ {
apply(*array_type.element_type); apply(*array_type.element_type);
} }
void apply(function_type const & function_type) void apply(ast::function_type const & function_type)
{ {
for (auto const & argument : function_type.arguments) for (auto const & argument : function_type.arguments)
apply(*argument); apply(*argument);
apply(*function_type.result); apply(*function_type.result);
} }
void apply(pointer_type const & pointer_type) void apply(ast::pointer_type const & pointer_type)
{ {
apply(*pointer_type.referenced_type); apply(*pointer_type.referenced_type);
} }
void apply(type_identifier & identifier) void apply(ast::type_identifier & identifier)
{ {
for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) for (auto it = scopes.rbegin(); it != scopes.rend(); ++it)
{ {
@ -166,13 +166,13 @@ namespace pslang::ast
} }
} }
throw parse_error("Identifier \"" + identifier.name + "\" not found", identifier.location); throw validation_error("Identifier \"" + identifier.name + "\" not found", identifier.location);
} }
void apply(literal const &) void apply(ast::literal const &)
{} {}
void apply(identifier & identifier) void apply(ast::identifier & identifier)
{ {
// NB: cannot be a type // NB: cannot be a type
// The case of type constructors is resolved earlier in function_call node // The case of type constructors is resolved earlier in function_call node
@ -216,29 +216,29 @@ namespace pslang::ast
crossed_function_scope |= it->is_function_scope; crossed_function_scope |= it->is_function_scope;
} }
throw parse_error("Identifier \"" + identifier.name + "\" not found", identifier.location); throw validation_error("Identifier \"" + identifier.name + "\" not found", identifier.location);
} }
void apply(unary_operation const & unary_operation) void apply(ast::unary_operation const & unary_operation)
{ {
apply(*unary_operation.arg1); apply(*unary_operation.arg1);
} }
void apply(binary_operation const & binary_operation) void apply(ast::binary_operation const & binary_operation)
{ {
apply(*binary_operation.arg1); apply(*binary_operation.arg1);
apply(*binary_operation.arg2); apply(*binary_operation.arg2);
} }
void apply(cast_operation const & cast_operation) void apply(ast::cast_operation const & cast_operation)
{ {
apply(*cast_operation.expression); apply(*cast_operation.expression);
apply(*cast_operation.type); apply(*cast_operation.type);
} }
void apply(function_call & function_call) void apply(ast::function_call & function_call)
{ {
if (auto id = std::get_if<identifier>(function_call.function.get())) if (auto id = std::get_if<ast::identifier>(function_call.function.get()))
{ {
if (auto type = types::builtin_type(id->name)) if (auto type = types::builtin_type(id->name))
{ {
@ -247,7 +247,7 @@ namespace pslang::ast
else if (auto primitive_type = std::get_if<types::primitive_type>(type.get())) else if (auto primitive_type = std::get_if<types::primitive_type>(type.get()))
function_call.type = std::make_unique<ast::type>(*primitive_type); function_call.type = std::make_unique<ast::type>(*primitive_type);
else else
throw invalid_ast_error("Unknown built-in type \"" + id->name + "\"", get_location(*function_call.function)); throw internal_error("Unknown built-in type \"" + id->name + "\"", get_location(*function_call.function));
function_call.function = nullptr; function_call.function = nullptr;
} }
@ -257,7 +257,7 @@ namespace pslang::ast
{ {
if (auto jt = it->structs.find(id->name); jt != it->structs.end()) if (auto jt = it->structs.find(id->name); jt != it->structs.end())
{ {
function_call.type = std::make_unique<ast::type>(type_identifier{.name = id->name, .location = id->location, .node = jt->second}); function_call.type = std::make_unique<ast::type>(ast::type_identifier{.name = id->name, .location = id->location, .node = jt->second});
function_call.function = nullptr; function_call.function = nullptr;
break; break;
} }
@ -273,31 +273,31 @@ namespace pslang::ast
apply(*argument); apply(*argument);
} }
void apply(array const & array) void apply(ast::array const & array)
{ {
for (auto const & element : array.elements) for (auto const & element : array.elements)
apply(*element); apply(*element);
} }
void apply(array_access const & array_access) void apply(ast::array_access const & array_access)
{ {
apply(*array_access.array); apply(*array_access.array);
apply(*array_access.index); apply(*array_access.index);
} }
void apply(field_access const & field_access) void apply(ast::field_access const & field_access)
{ {
apply(*field_access.object); apply(*field_access.object);
} }
void apply(if_expression const & if_expression) void apply(ast::if_expression const & if_expression)
{ {
apply(*if_expression.condition); apply(*if_expression.condition);
apply(*if_expression.if_true); apply(*if_expression.if_true);
apply(*if_expression.if_false); apply(*if_expression.if_false);
} }
void apply(sizeof_operator const & sizeof_operator) void apply(ast::sizeof_operator const & sizeof_operator)
{ {
if (sizeof_operator.expression) if (sizeof_operator.expression)
apply(*sizeof_operator.expression); apply(*sizeof_operator.expression);
@ -305,7 +305,7 @@ namespace pslang::ast
apply(*sizeof_operator.type); apply(*sizeof_operator.type);
} }
void apply(alignof_operator const & alignof_operator) void apply(ast::alignof_operator const & alignof_operator)
{ {
if (alignof_operator.expression) if (alignof_operator.expression)
apply(*alignof_operator.expression); apply(*alignof_operator.expression);
@ -313,27 +313,27 @@ namespace pslang::ast
apply(*alignof_operator.type); apply(*alignof_operator.type);
} }
void apply(expression_ptr const & expression_ptr) void apply(ast::expression_ptr const & expression_ptr)
{ {
apply(*expression_ptr); apply(*expression_ptr);
} }
void apply(assignment const & assignment) void apply(ast::assignment const & assignment)
{ {
apply(assignment.lhs); apply(assignment.lhs);
apply(assignment.rhs); apply(assignment.rhs);
} }
void apply(variable_declaration & variable_declaration) void apply(ast::variable_declaration & variable_declaration)
{ {
if (scopes.back().contains(variable_declaration.name)) if (scopes.back().contains(variable_declaration.name))
throw parse_error("Identifier \"" + variable_declaration.name + "\" is already defined at this scope", variable_declaration.location); throw validation_error("Identifier \"" + variable_declaration.name + "\" is already defined at this scope", variable_declaration.location);
if (variable_declaration.type) if (variable_declaration.type)
apply(*variable_declaration.type); apply(*variable_declaration.type);
apply(*variable_declaration.initializer); apply(*variable_declaration.initializer);
if (variable_declaration.category == value_category::compile_time) if (variable_declaration.category == ast::value_category::compile_time)
scopes.back().constants[variable_declaration.name] = &variable_declaration; scopes.back().constants[variable_declaration.name] = &variable_declaration;
else if (variable_declaration.global) else if (variable_declaration.global)
scopes.back().globals[variable_declaration.name] = &variable_declaration; scopes.back().globals[variable_declaration.name] = &variable_declaration;
@ -341,7 +341,7 @@ namespace pslang::ast
scopes.back().variables[variable_declaration.name] = &variable_declaration; scopes.back().variables[variable_declaration.name] = &variable_declaration;
} }
void apply(if_chain const & if_chain) void apply(ast::if_chain const & if_chain)
{ {
for (auto const & block : if_chain.blocks) for (auto const & block : if_chain.blocks)
{ {
@ -353,7 +353,7 @@ namespace pslang::ast
} }
} }
void apply(while_block const & while_block) void apply(ast::while_block const & while_block)
{ {
apply(*while_block.condition); apply(*while_block.condition);
scopes.emplace_back(); scopes.emplace_back();
@ -361,21 +361,21 @@ namespace pslang::ast
scopes.pop_back(); scopes.pop_back();
} }
void apply(break_statement const & break_statement) void apply(ast::break_statement const & break_statement)
{} {}
void apply(continue_statement const & continue_statement) void apply(ast::continue_statement const & continue_statement)
{} {}
void apply(function_definition & function_definition) void apply(ast::function_definition & function_definition)
{ {
// Already added to scope by populate_globals_visitor // Already added to scope by populate_globals_visitor
std::unordered_map<std::string, variable_base *> arguments; std::unordered_map<std::string, ast::variable_base *> arguments;
for (auto & argument : function_definition.arguments) for (auto & argument : function_definition.arguments)
{ {
if (arguments.count(argument.name) > 0) if (arguments.count(argument.name) > 0)
throw parse_error("Duplicate argument name \"" + argument.name + "\" in function \"" + function_definition.name + "\"", argument.location); throw validation_error("Duplicate argument name \"" + argument.name + "\" in function \"" + function_definition.name + "\"", argument.location);
arguments[argument.name] = &argument; arguments[argument.name] = &argument;
apply(*argument.type); apply(*argument.type);
} }
@ -389,7 +389,7 @@ namespace pslang::ast
scopes.pop_back(); scopes.pop_back();
} }
void apply(foreign_function_declaration const & foreign_function_declaration) void apply(ast::foreign_function_declaration const & foreign_function_declaration)
{ {
// Already added to scope by populate_globals_visitor // Already added to scope by populate_globals_visitor
@ -397,7 +397,7 @@ namespace pslang::ast
for (auto const & argument : foreign_function_declaration.arguments) for (auto const & argument : foreign_function_declaration.arguments)
{ {
if (argument_names.count(argument.name) > 0) if (argument_names.count(argument.name) > 0)
throw parse_error("Duplicate argument name \"" + argument.name + "\" in function \"" + foreign_function_declaration.name + "\"", argument.location); throw validation_error("Duplicate argument name \"" + argument.name + "\" in function \"" + foreign_function_declaration.name + "\"", argument.location);
argument_names.insert(argument.name); argument_names.insert(argument.name);
apply(*argument.type); apply(*argument.type);
} }
@ -405,13 +405,13 @@ namespace pslang::ast
apply(*foreign_function_declaration.return_type); apply(*foreign_function_declaration.return_type);
} }
void apply(return_statement const & return_statement) void apply(ast::return_statement const & return_statement)
{ {
if (return_statement.value) if (return_statement.value)
apply(*return_statement.value); apply(*return_statement.value);
} }
void apply(struct_definition & struct_definition) void apply(ast::struct_definition & struct_definition)
{ {
// Already added to scope by populate_globals_visitor // Already added to scope by populate_globals_visitor
@ -421,7 +421,7 @@ namespace pslang::ast
scopes.back().structs[struct_definition.name] = &struct_definition; scopes.back().structs[struct_definition.name] = &struct_definition;
} }
void apply(statement_list & statement_list) void apply(ast::statement_list & statement_list)
{ {
populate_globals_visitor populate_globals_visitor{{}, scopes}; populate_globals_visitor populate_globals_visitor{{}, scopes};
populate_globals_visitor.apply(statement_list); populate_globals_visitor.apply(statement_list);
@ -433,12 +433,17 @@ namespace pslang::ast
} }
void resolve_identifiers(statement_ptr & root) void resolve_identifiers(module & module)
{ {
std::vector<scope> scopes; std::vector<scope> scopes;
scopes.emplace_back(); scopes.emplace_back();
resolve_identifiers_visitor visitor{{}, {}, {}, scopes}; resolve_identifiers_visitor visitor{{}, {}, {}, scopes};
visitor.apply(*root); visitor.apply(*module.statements);
module.exported_structs = std::move(scopes.back().structs);
module.exported_functions = std::move(scopes.back().functions);
module.exported_foreign_functions = std::move(scopes.back().foreign_functions);
module.exported_globals = std::move(scopes.back().globals);
} }
} }

View file

@ -1,8 +1,8 @@
#include <pslang/ast/preprocess.hpp> #include <pslang/semantic/preprocess.hpp>
#include <pslang/semantic/error.hpp>
#include <pslang/ast/type_visitor.hpp> #include <pslang/ast/type_visitor.hpp>
#include <pslang/ast/expression_visitor.hpp> #include <pslang/ast/expression_visitor.hpp>
#include <pslang/ast/statement_visitor.hpp> #include <pslang/ast/statement_visitor.hpp>
#include <pslang/ast/error.hpp>
#include <pslang/ast/print.hpp> #include <pslang/ast/print.hpp>
#include <pslang/types/type.hpp> #include <pslang/types/type.hpp>
#include <pslang/types/type_visitor.hpp> #include <pslang/types/type_visitor.hpp>
@ -10,7 +10,7 @@
#include <unordered_map> #include <unordered_map>
#include <sstream> #include <sstream>
namespace pslang::ast namespace pslang::semantic
{ {
namespace namespace
@ -107,7 +107,7 @@ namespace pslang::ast
} }
struct resolve_types_visitor struct resolve_types_visitor
: type_visitor<resolve_types_visitor> : ast::type_visitor<resolve_types_visitor>
{ {
using type_visitor::apply; using type_visitor::apply;
@ -159,34 +159,34 @@ namespace pslang::ast
} }
struct populate_globals_visitor struct populate_globals_visitor
: statement_visitor<populate_globals_visitor> : ast::statement_visitor<populate_globals_visitor>
{ {
local_context & lcontext; local_context & lcontext;
using statement_visitor::apply; using statement_visitor::apply;
void apply(expression_ptr const &) void apply(ast::expression_ptr const &)
{} {}
void apply(assignment const &) void apply(ast::assignment const &)
{} {}
void apply(variable_declaration const &) void apply(ast::variable_declaration const &)
{} {}
void apply(if_chain const &) void apply(ast::if_chain const &)
{} {}
void apply(while_block const &) void apply(ast::while_block const &)
{} {}
void apply(break_statement const &) void apply(ast::break_statement const &)
{} {}
void apply(continue_statement const &) void apply(ast::continue_statement const &)
{} {}
void apply(function_definition & node) void apply(ast::function_definition & node)
{ {
types::function_type function_type; types::function_type function_type;
resolve_types(*node.return_type); resolve_types(*node.return_type);
@ -201,7 +201,7 @@ namespace pslang::ast
node.inferred_function_type = std::make_unique<types::type>(std::move(function_type)); node.inferred_function_type = std::make_unique<types::type>(std::move(function_type));
} }
void apply(foreign_function_declaration & node) void apply(ast::foreign_function_declaration & node)
{ {
types::function_type function_type; types::function_type function_type;
resolve_types(*node.return_type); resolve_types(*node.return_type);
@ -216,10 +216,10 @@ namespace pslang::ast
node.inferred_function_type = std::make_unique<types::type>(std::move(function_type)); node.inferred_function_type = std::make_unique<types::type>(std::move(function_type));
} }
void apply(return_statement const &) void apply(ast::return_statement const &)
{} {}
void apply(struct_definition & node) void apply(ast::struct_definition & node)
{ {
for (auto & field : node.fields) for (auto & field : node.fields)
{ {
@ -233,24 +233,24 @@ namespace pslang::ast
} }
}; };
void populate_globals(local_context & lcontext, statement_list & statement_list) void populate_globals(local_context & lcontext, ast::statement_list & statement_list)
{ {
populate_globals_visitor{{}, lcontext}.apply(statement_list); populate_globals_visitor{{}, lcontext}.apply(statement_list);
} }
struct check_visitor struct check_visitor
: expression_visitor<check_visitor> : ast::expression_visitor<check_visitor>
, statement_visitor<check_visitor> , ast::statement_visitor<check_visitor>
{ {
local_context & lcontext; local_context & lcontext;
using expression_visitor::apply; using expression_visitor::apply;
using statement_visitor::apply; using statement_visitor::apply;
void apply(literal &) void apply(ast::literal &)
{} {}
void apply(identifier & node) void apply(ast::identifier & node)
{ {
if (node.constant_node) if (node.constant_node)
{ {
@ -273,31 +273,31 @@ namespace pslang::ast
node.inferred_type = node.foreign_function_node->inferred_function_type; node.inferred_type = node.foreign_function_node->inferred_function_type;
} }
else else
throw invalid_ast_error("Identifier node without a variable/function/foreign function reference", node.location); throw internal_error("Identifier node without a variable/function/foreign function reference", node.location);
} }
void apply(unary_operation & node) void apply(ast::unary_operation & node)
{ {
apply(*node.arg1); apply(*node.arg1);
auto arg1_type = get_type(*node.arg1); auto arg1_type = get_type(*node.arg1);
switch (node.type) switch (node.type)
{ {
case unary_operation_type::negation: case ast::unary_operation_type::negation:
if (types::is_integer_type(*arg1_type) || types::is_floating_point_type(*arg1_type)) if (types::is_integer_type(*arg1_type) || types::is_floating_point_type(*arg1_type))
{ {
node.inferred_type = arg1_type; node.inferred_type = arg1_type;
return; return;
} }
break; break;
case unary_operation_type::logical_not: case ast::unary_operation_type::logical_not:
if (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type)) if (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type))
{ {
node.inferred_type = arg1_type; node.inferred_type = arg1_type;
return; return;
} }
break; break;
case unary_operation_type::address_of: case ast::unary_operation_type::address_of:
if (auto lvalue = classify_lvalue(node.arg1)) if (auto lvalue = classify_lvalue(node.arg1))
{ {
switch (*lvalue) switch (*lvalue)
@ -313,7 +313,7 @@ namespace pslang::ast
else else
throw type_error("Cannot take address of a non-lvalue", node.location); throw type_error("Cannot take address of a non-lvalue", node.location);
break; break;
case unary_operation_type::mutable_address_of: case ast::unary_operation_type::mutable_address_of:
if (auto lvalue = classify_lvalue(node.arg1)) if (auto lvalue = classify_lvalue(node.arg1))
{ {
switch (*lvalue) switch (*lvalue)
@ -330,7 +330,7 @@ namespace pslang::ast
else else
throw type_error("Cannot take address of a non-lvalue", node.location); throw type_error("Cannot take address of a non-lvalue", node.location);
break; break;
case unary_operation_type::dereference: case ast::unary_operation_type::dereference:
if (auto pointer_type = std::get_if<types::pointer_type>(arg1_type.get())) if (auto pointer_type = std::get_if<types::pointer_type>(arg1_type.get()))
{ {
node.inferred_type = pointer_type->referenced_type; node.inferred_type = pointer_type->referenced_type;
@ -341,11 +341,11 @@ namespace pslang::ast
std::ostringstream os; std::ostringstream os;
os << "Cannot apply " << node.type << " to a value of type "; os << "Cannot apply " << node.type << " to a value of type ";
print(os, *arg1_type); ast::print(os, *arg1_type);
throw type_error(os.str(), node.location); throw type_error(os.str(), node.location);
} }
void apply(binary_operation & node) void apply(ast::binary_operation & node)
{ {
apply(*node.arg1); apply(*node.arg1);
apply(*node.arg2); apply(*node.arg2);
@ -358,7 +358,7 @@ namespace pslang::ast
switch (node.type) switch (node.type)
{ {
case binary_operation_type::addition: case ast::binary_operation_type::addition:
if (equal && types::is_numeric_type(*arg1_type)) if (equal && types::is_numeric_type(*arg1_type))
{ {
node.inferred_type = arg1_type; node.inferred_type = arg1_type;
@ -379,7 +379,7 @@ namespace pslang::ast
return; return;
} }
break; break;
case binary_operation_type::subtraction: case ast::binary_operation_type::subtraction:
if (equal && types::is_numeric_type(*arg1_type)) if (equal && types::is_numeric_type(*arg1_type))
{ {
node.inferred_type = arg1_type; node.inferred_type = arg1_type;
@ -400,64 +400,64 @@ namespace pslang::ast
return; return;
} }
break; break;
case binary_operation_type::multiplication: case ast::binary_operation_type::multiplication:
if (equal && types::is_numeric_type(*arg1_type)) if (equal && types::is_numeric_type(*arg1_type))
{ {
node.inferred_type = arg1_type; node.inferred_type = arg1_type;
return; return;
} }
break; break;
case binary_operation_type::division: case ast::binary_operation_type::division:
if (equal && types::is_numeric_type(*arg1_type)) if (equal && types::is_numeric_type(*arg1_type))
{ {
node.inferred_type = arg1_type; node.inferred_type = arg1_type;
return; return;
} }
break; break;
case binary_operation_type::remainder: case ast::binary_operation_type::remainder:
if (equal && types::is_integer_type(*arg1_type)) if (equal && types::is_integer_type(*arg1_type))
{ {
node.inferred_type = arg1_type; node.inferred_type = arg1_type;
return; return;
} }
break; break;
case binary_operation_type::binary_and: case ast::binary_operation_type::binary_and:
if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type))) if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type)))
{ {
node.inferred_type = arg1_type; node.inferred_type = arg1_type;
return; return;
} }
break; break;
case binary_operation_type::logical_and: case ast::binary_operation_type::logical_and:
if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type))) if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type)))
{ {
node.inferred_type = arg1_type; node.inferred_type = arg1_type;
return; return;
} }
break; break;
case binary_operation_type::binary_or: case ast::binary_operation_type::binary_or:
if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type))) if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type)))
{ {
node.inferred_type = arg1_type; node.inferred_type = arg1_type;
return; return;
} }
break; break;
case binary_operation_type::logical_or: case ast::binary_operation_type::logical_or:
if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type))) if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type)))
{ {
node.inferred_type = arg1_type; node.inferred_type = arg1_type;
return; return;
} }
break; break;
case binary_operation_type::logical_xor: case ast::binary_operation_type::logical_xor:
if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type))) if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type)))
{ {
node.inferred_type = arg1_type; node.inferred_type = arg1_type;
return; return;
} }
break; break;
case binary_operation_type::left_shift: case ast::binary_operation_type::left_shift:
case binary_operation_type::right_shift: case ast::binary_operation_type::right_shift:
if (both_integers) if (both_integers)
{ {
if (!types::is_unsigned_integer_type(*arg2_type)) if (!types::is_unsigned_integer_type(*arg2_type))
@ -471,42 +471,42 @@ namespace pslang::ast
return; return;
} }
break; break;
case binary_operation_type::equals: case ast::binary_operation_type::equals:
if (equal || both_integers) if (equal || both_integers)
{ {
node.inferred_type = std::make_unique<types::type>(types::bool_type{}); node.inferred_type = std::make_unique<types::type>(types::bool_type{});
return; return;
} }
break; break;
case binary_operation_type::not_equals: case ast::binary_operation_type::not_equals:
if (equal || both_integers) if (equal || both_integers)
{ {
node.inferred_type = std::make_unique<types::type>(types::bool_type{}); node.inferred_type = std::make_unique<types::type>(types::bool_type{});
return; return;
} }
break; break;
case binary_operation_type::less: case ast::binary_operation_type::less:
if (equal || both_integers) if (equal || both_integers)
{ {
node.inferred_type = std::make_unique<types::type>(types::bool_type{}); node.inferred_type = std::make_unique<types::type>(types::bool_type{});
return; return;
} }
break; break;
case binary_operation_type::greater: case ast::binary_operation_type::greater:
if (equal || both_integers) if (equal || both_integers)
{ {
node.inferred_type = std::make_unique<types::type>(types::bool_type{}); node.inferred_type = std::make_unique<types::type>(types::bool_type{});
return; return;
} }
break; break;
case binary_operation_type::less_equals: case ast::binary_operation_type::less_equals:
if (equal || both_integers) if (equal || both_integers)
{ {
node.inferred_type = std::make_unique<types::type>(types::bool_type{}); node.inferred_type = std::make_unique<types::type>(types::bool_type{});
return; return;
} }
break; break;
case binary_operation_type::greater_equals: case ast::binary_operation_type::greater_equals:
if (equal || both_integers) if (equal || both_integers)
{ {
node.inferred_type = std::make_unique<types::type>(types::bool_type{}); node.inferred_type = std::make_unique<types::type>(types::bool_type{});
@ -517,13 +517,13 @@ namespace pslang::ast
std::ostringstream os; std::ostringstream os;
os << "Cannot apply " << node.type << " to values of types "; os << "Cannot apply " << node.type << " to values of types ";
print(os, *arg1_type); ast::print(os, *arg1_type);
os << " and "; os << " and ";
print(os, *arg2_type); ast::print(os, *arg2_type);
throw type_error(os.str(), node.location); throw type_error(os.str(), node.location);
} }
void apply(cast_operation & node) void apply(ast::cast_operation & node)
{ {
apply(*node.expression); apply(*node.expression);
resolve_types(*node.type); resolve_types(*node.type);
@ -581,13 +581,13 @@ namespace pslang::ast
std::ostringstream os; std::ostringstream os;
os << "Cannot cast a value of type "; os << "Cannot cast a value of type ";
print(os, *source_type); ast::print(os, *source_type);
os << " to type "; os << " to type ";
print(os, *target_type); ast::print(os, *target_type);
throw type_error(os.str(), node.location); throw type_error(os.str(), node.location);
} }
void apply(function_call & node) void apply(ast::function_call & node)
{ {
if (node.function) if (node.function)
apply(*node.function); apply(*node.function);
@ -610,7 +610,7 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "Cannot call a value of a non-function type "; os << "Cannot call a value of a non-function type ";
print(os, *function_type); ast::print(os, *function_type);
throw type_error(os.str(), node.location); throw type_error(os.str(), node.location);
} }
@ -618,7 +618,7 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "Cannot call function " << function_name << "of type "; os << "Cannot call function " << function_name << "of type ";
print(os, *function_type); ast::print(os, *function_type);
os << ": expected " << ftype->arguments.size() << " arguments, but got " << node.arguments.size(); os << ": expected " << ftype->arguments.size() << " arguments, but got " << node.arguments.size();
throw type_error(os.str(), node.location); throw type_error(os.str(), node.location);
} }
@ -630,11 +630,11 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "Cannot call function " << function_name << "of type "; os << "Cannot call function " << function_name << "of type ";
print(os, *function_type); ast::print(os, *function_type);
os << ": argument #" << i << " expected to have type "; os << ": argument #" << i << " expected to have type ";
print(os, *ftype->arguments[i]); ast::print(os, *ftype->arguments[i]);
os << " but got type "; os << " but got type ";
print(os, *arg_type); ast::print(os, *arg_type);
throw type_error(os.str(), get_location(*node.arguments[i])); throw type_error(os.str(), get_location(*node.arguments[i]));
} }
} }
@ -655,7 +655,7 @@ namespace pslang::ast
std::ostringstream os; std::ostringstream os;
os << "Cannot create built-in type "; os << "Cannot create built-in type ";
print(os, *type); ast::print(os, *type);
os << ": expected 0 arguments, but got " << node.arguments.size(); os << ": expected 0 arguments, but got " << node.arguments.size();
throw type_error(os.str(), node.location); throw type_error(os.str(), node.location);
} }
@ -675,9 +675,9 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "Cannot create struct " << struct_node.name << ": argument #" << i << " expected to have type "; os << "Cannot create struct " << struct_node.name << ": argument #" << i << " expected to have type ";
print(os, *struct_node.fields[i].inferred_type); ast::print(os, *struct_node.fields[i].inferred_type);
os << " but got type "; os << " but got type ";
print(os, *arg_type); ast::print(os, *arg_type);
throw type_error(os.str(), node.location); throw type_error(os.str(), node.location);
} }
} }
@ -688,13 +688,13 @@ namespace pslang::ast
} }
} }
else else
throw invalid_ast_error("Function call node has neither function nor type", node.location); throw internal_error("Function call node has neither function nor type", node.location);
} }
void apply(array & node) void apply(ast::array & node)
{ {
if (node.elements.empty()) if (node.elements.empty())
throw invalid_ast_error("Empty array", node.location); throw internal_error("Empty array", node.location);
for (auto const & element : node.elements) for (auto const & element : node.elements)
apply(*element); apply(*element);
@ -712,9 +712,9 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "Failed to infer array type: element #0 has type "; os << "Failed to infer array type: element #0 has type ";
print(os, *element_type); ast::print(os, *element_type);
os << " but element #" << i << " has type "; os << " but element #" << i << " has type ";
print(os, *current_type); ast::print(os, *current_type);
} }
} }
@ -724,7 +724,7 @@ namespace pslang::ast
node.inferred_type = std::make_unique<types::type>(std::move(type)); node.inferred_type = std::make_unique<types::type>(std::move(type));
} }
void apply(array_access & node) void apply(ast::array_access & node)
{ {
apply(*node.array); apply(*node.array);
apply(*node.index); apply(*node.index);
@ -736,7 +736,7 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "Expected an integer type as index, but got "; os << "Expected an integer type as index, but got ";
print(os, *index_type); ast::print(os, *index_type);
throw type_error(os.str(), get_location(*node.index)); throw type_error(os.str(), get_location(*node.index));
} }
@ -754,11 +754,11 @@ namespace pslang::ast
std::ostringstream os; std::ostringstream os;
os << "Expected an array or a pointer, but got "; os << "Expected an array or a pointer, but got ";
print(os, *array_type); ast::print(os, *array_type);
throw type_error(os.str(), get_location(*node.array)); throw type_error(os.str(), get_location(*node.array));
} }
void apply(field_access & node) void apply(ast::field_access & node)
{ {
apply(*node.object); apply(*node.object);
@ -773,7 +773,7 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "Expected a struct, but got "; os << "Expected a struct, but got ";
print(os, *object_type); ast::print(os, *object_type);
throw type_error(os.str(), get_location(*node.object)); throw type_error(os.str(), get_location(*node.object));
} }
@ -791,7 +791,7 @@ namespace pslang::ast
throw type_error(std::format("Struct \"{}\" has no field named \"{}\"", struct_node.name, node.field_name), node.location); throw type_error(std::format("Struct \"{}\" has no field named \"{}\"", struct_node.name, node.field_name), node.location);
} }
void apply(if_expression & node) void apply(ast::if_expression & node)
{ {
apply(*node.condition); apply(*node.condition);
apply(*node.if_true); apply(*node.if_true);
@ -802,7 +802,7 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "if condition expects a bool type, but got "; os << "if condition expects a bool type, but got ";
print(os, *condition_type); ast::print(os, *condition_type);
throw type_error(os.str(), get_location(*node.condition)); throw type_error(os.str(), get_location(*node.condition));
} }
@ -813,19 +813,19 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "Both ternary if cases must have the same type, but got "; os << "Both ternary if cases must have the same type, but got ";
print(os, *true_type); ast::print(os, *true_type);
os << " and "; os << " and ";
print(os, *false_type); ast::print(os, *false_type);
throw type_error(os.str(), node.location); throw type_error(os.str(), node.location);
} }
node.inferred_type = true_type; node.inferred_type = true_type;
} }
void apply(sizeof_operator & node) void apply(ast::sizeof_operator & node)
{ {
if (node.expression && node.type) if (node.expression && node.type)
throw invalid_ast_error("sizeof node cannot have both an expression and a type", node.location); throw internal_error("sizeof node cannot have both an expression and a type", node.location);
if (node.expression) if (node.expression)
{ {
@ -842,10 +842,10 @@ namespace pslang::ast
node.inferred_type = std::make_shared<types::type>(types::primitive_type{types::u64_type{}}); node.inferred_type = std::make_shared<types::type>(types::primitive_type{types::u64_type{}});
} }
void apply(alignof_operator & node) void apply(ast::alignof_operator & node)
{ {
if (node.expression && node.type) if (node.expression && node.type)
throw invalid_ast_error("alignof node cannot have both an expression and a type", node.location); throw internal_error("alignof node cannot have both an expression and a type", node.location);
if (node.expression) if (node.expression)
{ {
@ -862,12 +862,12 @@ namespace pslang::ast
node.inferred_type = std::make_shared<types::type>(types::primitive_type{types::u64_type{}}); node.inferred_type = std::make_shared<types::type>(types::primitive_type{types::u64_type{}});
} }
void apply(expression_ptr const & node) void apply(ast::expression_ptr const & node)
{ {
apply(*node); apply(*node);
} }
void apply(assignment const & node) void apply(ast::assignment const & node)
{ {
apply(node.lhs); apply(node.lhs);
apply(node.rhs); apply(node.rhs);
@ -888,14 +888,14 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "Cannot assign a value of type "; os << "Cannot assign a value of type ";
print(os, *rtype); ast::print(os, *rtype);
os << " to an expression of type "; os << " to an expression of type ";
print(os, *ltype); ast::print(os, *ltype);
throw type_error(os.str(), node.location); throw type_error(os.str(), node.location);
}; };
} }
void apply(variable_declaration & node) void apply(ast::variable_declaration & node)
{ {
apply(node.initializer); apply(node.initializer);
node.inferred_type = get_type(*node.initializer); node.inferred_type = get_type(*node.initializer);
@ -907,15 +907,15 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "Cannot initialize a variable of type "; os << "Cannot initialize a variable of type ";
print(os, *expected_type); ast::print(os, *expected_type);
os << " with an expression of type "; os << " with an expression of type ";
print(os, *node.inferred_type); ast::print(os, *node.inferred_type);
throw type_error(os.str(), node.location); throw type_error(os.str(), node.location);
} }
} }
} }
void apply(if_chain const & node) void apply(ast::if_chain const & node)
{ {
for (auto const & block : node.blocks) for (auto const & block : node.blocks)
{ {
@ -927,7 +927,7 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "if condition expects a bool type, but got "; os << "if condition expects a bool type, but got ";
print(os, *actual_type); ast::print(os, *actual_type);
throw type_error(os.str(), get_location(*block.condition)); throw type_error(os.str(), get_location(*block.condition));
} }
} }
@ -936,7 +936,7 @@ namespace pslang::ast
} }
} }
void apply(while_block const & node) void apply(ast::while_block const & node)
{ {
apply(node.condition); apply(node.condition);
auto actual_type = get_type(*node.condition); auto actual_type = get_type(*node.condition);
@ -944,28 +944,28 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "while condition expects a bool type, but got "; os << "while condition expects a bool type, but got ";
print(os, *actual_type); ast::print(os, *actual_type);
throw type_error(os.str(), get_location(*node.condition)); throw type_error(os.str(), get_location(*node.condition));
} }
apply(*node.statements); apply(*node.statements);
} }
void apply(break_statement const &) void apply(ast::break_statement const &)
{} {}
void apply(continue_statement const &) void apply(ast::continue_statement const &)
{} {}
void apply(function_definition & node) void apply(ast::function_definition & node)
{ {
apply(*node.statements); apply(*node.statements);
} }
void apply(foreign_function_declaration & node) void apply(ast::foreign_function_declaration & node)
{} {}
void apply(return_statement const & node) void apply(ast::return_statement const & node)
{ {
types::type_ptr actual_type; types::type_ptr actual_type;
if (node.value) if (node.value)
@ -984,17 +984,17 @@ namespace pslang::ast
{ {
std::ostringstream os; std::ostringstream os;
os << "Returning value of type "; os << "Returning value of type ";
print(os, *actual_type); ast::print(os, *actual_type);
os << " from a function returning "; os << " from a function returning ";
print(os, *return_node.inferred_result_type); ast::print(os, *return_node.inferred_result_type);
throw type_error(os.str(), node.location); throw type_error(os.str(), node.location);
} }
} }
void apply(struct_definition const & node) void apply(ast::struct_definition const & node)
{} {}
void apply(statement_list & node) void apply(ast::statement_list & node)
{ {
populate_globals(lcontext, node); populate_globals(lcontext, node);
@ -1008,7 +1008,7 @@ namespace pslang::ast
} }
private: private:
std::optional<ast::value_category> classify_lvalue(expression_ptr const & node) std::optional<ast::value_category> classify_lvalue(ast::expression_ptr const & node)
{ {
if (auto identifier = std::get_if<ast::identifier>(node.get())) if (auto identifier = std::get_if<ast::identifier>(node.get()))
{ {
@ -1063,11 +1063,11 @@ namespace pslang::ast
} }
void check_and_infer_types(statement_ptr & root) void check_and_infer_types(module & module)
{ {
local_context lcontext; local_context lcontext;
check_visitor visitor{{}, {}, lcontext}; check_visitor visitor{{}, {}, lcontext};
visitor.apply(*root); visitor.apply(*module.statements);
} }
} }

View file

@ -0,0 +1,66 @@
#include <pslang/semantic/preprocess.hpp>
#include <pslang/semantic/error.hpp>
#include <pslang/ast/statement_visitor.hpp>
#include <pslang/types/type.hpp>
namespace pslang::semantic
{
namespace
{
struct validate_visitor
: ast::const_statement_visitor<validate_visitor>
{
using const_statement_visitor::apply;
void apply(ast::expression_ptr const &) {}
void apply(ast::assignment const &) {}
void apply(ast::variable_declaration const &) {}
void apply(ast::if_chain const & node)
{
for (auto const & block : node.blocks)
apply(*block.statements);
}
void apply(ast::while_block const & node)
{
apply(*node.statements);
}
void apply(ast::break_statement const &)
{}
void apply(ast::continue_statement const &)
{}
void apply(ast::function_definition const & node)
{
apply(*node.statements);
if (!types::equal(*get_type(*node.return_type), types::unit_type{}))
if (node.statements->statements.empty() || (true
&& !std::get_if<ast::return_statement>(node.statements->statements.back().get())
&& !std::get_if<ast::if_chain>(node.statements->statements.back().get())
&& !std::get_if<ast::while_block>(node.statements->statements.back().get())
))
throw validation_error("Function returning non-unit is missing a return statement in the end", node.location);
}
void apply(ast::foreign_function_declaration const &) {}
void apply(ast::return_statement const &) {}
void apply(ast::struct_definition const &) {}
};
}
void validate(module & module)
{
validate_visitor{}.apply(*module.statements);
}
}