From 800de413cfd188f4af3b711953eb5547572f91b6 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 3 Apr 2026 00:04:12 +0300 Subject: [PATCH] Add sizeof & alignof builtins --- examples/raytracer.psl | 9 +-- libs/ast/include/pslang/ast/builtins.hpp | 29 +++++++ libs/ast/include/pslang/ast/expression.hpp | 5 +- libs/ast/source/expression.cpp | 92 +--------------------- libs/ast/source/print.cpp | 32 ++++++++ libs/ast/source/resolve_identifiers.cpp | 16 ++++ libs/ast/source/type_check.cpp | 40 ++++++++++ libs/interpreter/source/eval.cpp | 20 +++++ libs/ir/source/compiler.cpp | 12 +++ libs/parser/rules/pslang.l | 2 + libs/parser/rules/pslang.y | 4 + plans.txt | 3 +- spec.txt | 9 +++ 13 files changed, 178 insertions(+), 95 deletions(-) create mode 100644 libs/ast/include/pslang/ast/builtins.hpp diff --git a/examples/raytracer.psl b/examples/raytracer.psl index a5f93a4..3733731 100644 --- a/examples/raytracer.psl +++ b/examples/raytracer.psl @@ -670,8 +670,7 @@ func unlock_mutex(mutex: mutex mut*): func make_default_scene() -> scene: let object_count = 8ul - // No sizeof yet... - let objects = allocate(object_count * 20ul * 4ul) as object mut* + let objects = allocate(object_count * sizeof(object)) as object mut* // Filling all objects in the same function overflows the max stack size // dictated by arm64 limitations (add immediate is bound by 12 bits => 4Kb) @@ -814,8 +813,8 @@ func main(): mut report_count = 0u let thread_count = 9ul - let threads = allocate(thread_count * 8ul) as thread mut* - let threads_data = allocate(thread_count * 104ul) as thread_data mut* + let threads = allocate(thread_count * sizeof(thread)) as thread mut* + let threads_data = allocate(thread_count * sizeof(thread_data)) as thread_data mut* mut th = 0ul while th < thread_count: @@ -825,7 +824,7 @@ func main(): data.camera = camera data.ystart = th data.ystep = thread_count - data.done_mutex = allocate(64ul) as mutex mut* + data.done_mutex = allocate(sizeof(mutex)) as mutex mut* create_mutex(data.done_mutex) data.done = 0ul threads[th] = create_thread(thread_func, data as unit mut*) diff --git a/libs/ast/include/pslang/ast/builtins.hpp b/libs/ast/include/pslang/ast/builtins.hpp new file mode 100644 index 0000000..b2c0af5 --- /dev/null +++ b/libs/ast/include/pslang/ast/builtins.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include + +namespace pslang::ast +{ + + struct sizeof_operator + { + expression_ptr expression; + type_ptr type; + location location; + types::type_ptr inferred_source_type = nullptr; + types::type_ptr inferred_type = nullptr; + }; + + struct alignof_operator + { + expression_ptr expression; + type_ptr type; + location location; + types::type_ptr inferred_source_type = nullptr; + types::type_ptr inferred_type = nullptr; + }; + +} \ No newline at end of file diff --git a/libs/ast/include/pslang/ast/expression.hpp b/libs/ast/include/pslang/ast/expression.hpp index b4081b4..601d342 100644 --- a/libs/ast/include/pslang/ast/expression.hpp +++ b/libs/ast/include/pslang/ast/expression.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -42,7 +43,9 @@ namespace pslang::ast array, array_access, field_access, - if_expression + if_expression, + sizeof_operator, + alignof_operator >; struct expression diff --git a/libs/ast/source/expression.cpp b/libs/ast/source/expression.cpp index a1ad6ec..f22dc82 100644 --- a/libs/ast/source/expression.cpp +++ b/libs/ast/source/expression.cpp @@ -13,53 +13,8 @@ namespace pslang::ast { using const_expression_visitor::apply; - template - location apply(primitive_literal_base const & node) - { - return node.location; - } - - location apply(identifier const & node) - { - return node.location; - } - - location apply(unary_operation const & node) - { - return node.location; - } - - location apply(binary_operation const & node) - { - return node.location; - } - - location apply(cast_operation const & node) - { - return node.location; - } - - location apply(function_call const & node) - { - return node.location; - } - - location apply(array const & node) - { - return node.location; - } - - location apply(array_access const & node) - { - return node.location; - } - - location apply(field_access const & node) - { - return node.location; - } - - location apply(if_expression const & node) + template + location apply(Node const & node) { return node.location; } @@ -76,47 +31,8 @@ namespace pslang::ast return std::make_unique(types::primitive_type{types::primitive_type_base{}}); } - types::type_ptr apply(identifier const & node) - { - return node.inferred_type; - } - - types::type_ptr apply(unary_operation const & node) - { - return node.inferred_type; - } - - types::type_ptr apply(binary_operation const & node) - { - return node.inferred_type; - } - - types::type_ptr apply(cast_operation const & node) - { - return node.inferred_type; - } - - types::type_ptr apply(function_call const & node) - { - return node.inferred_type; - } - - types::type_ptr apply(array const & node) - { - return node.inferred_type; - } - - types::type_ptr apply(array_access const & node) - { - return node.inferred_type; - } - - types::type_ptr apply(field_access const & node) - { - return node.inferred_type; - } - - types::type_ptr apply(if_expression const & node) + template + types::type_ptr apply(Node const & node) { return node.inferred_type; } diff --git a/libs/ast/source/print.cpp b/libs/ast/source/print.cpp index df198ac..8034c50 100644 --- a/libs/ast/source/print.cpp +++ b/libs/ast/source/print.cpp @@ -367,6 +367,38 @@ namespace pslang::ast child(*node.if_true); child(*node.if_false); } + + void apply(sizeof_operator const & node) + { + put_indent(out, options); + if (node.expression) + { + out << "sizeof\n"; + child(*node.expression); + } + else + { + out << "sizeof "; + print(out, *node.type); + out << "\n"; + } + } + + void apply(alignof_operator const & node) + { + put_indent(out, options); + if (node.expression) + { + out << "alignof\n"; + child(*node.expression); + } + else + { + out << "sizeof "; + print(out, *node.type); + out << "\n"; + } + } }; struct statement_print_visitor diff --git a/libs/ast/source/resolve_identifiers.cpp b/libs/ast/source/resolve_identifiers.cpp index 3844949..50ecb51 100644 --- a/libs/ast/source/resolve_identifiers.cpp +++ b/libs/ast/source/resolve_identifiers.cpp @@ -288,6 +288,22 @@ namespace pslang::ast apply(*if_expression.if_false); } + void apply(sizeof_operator const & sizeof_operator) + { + if (sizeof_operator.expression) + apply(*sizeof_operator.expression); + if (sizeof_operator.type) + apply(*sizeof_operator.type); + } + + void apply(alignof_operator const & alignof_operator) + { + if (alignof_operator.expression) + apply(*alignof_operator.expression); + if (alignof_operator.type) + apply(*alignof_operator.type); + } + void apply(expression_ptr const & expression_ptr) { apply(*expression_ptr); diff --git a/libs/ast/source/type_check.cpp b/libs/ast/source/type_check.cpp index dc1ddab..3192a19 100644 --- a/libs/ast/source/type_check.cpp +++ b/libs/ast/source/type_check.cpp @@ -818,6 +818,46 @@ namespace pslang::ast node.inferred_type = true_type; } + void apply(sizeof_operator & node) + { + if (node.expression && node.type) + throw invalid_ast_error("sizeof node cannot have both an expression and a type", node.location); + + if (node.expression) + { + apply(*node.expression); + node.inferred_source_type = get_type(*node.expression); + } + + if (node.type) + { + resolve_types(*node.type); + node.inferred_source_type = get_type(*node.type); + } + + node.inferred_type = std::make_shared(types::primitive_type{types::u64_type{}}); + } + + void apply(alignof_operator & node) + { + if (node.expression && node.type) + throw invalid_ast_error("alignof node cannot have both an expression and a type", node.location); + + if (node.expression) + { + apply(*node.expression); + node.inferred_source_type = get_type(*node.expression); + } + + if (node.type) + { + resolve_types(*node.type); + node.inferred_source_type = get_type(*node.type); + } + + node.inferred_type = std::make_shared(types::primitive_type{types::u64_type{}}); + } + void apply(expression_ptr const & node) { apply(*node); diff --git a/libs/interpreter/source/eval.cpp b/libs/interpreter/source/eval.cpp index f2e55d3..d1c30d1 100644 --- a/libs/interpreter/source/eval.cpp +++ b/libs/interpreter/source/eval.cpp @@ -691,6 +691,16 @@ namespace pslang::interpreter throw std::runtime_error("Not implemented"); } + value eval_impl(context & context, ast::sizeof_operator const & sizeof_operator) + { + throw std::runtime_error("Not implemented"); + } + + value eval_impl(context & context, ast::alignof_operator const & alignof_operator) + { + throw std::runtime_error("Not implemented"); + } + value eval_impl(context & context, ast::expression_ptr const & expression) { return std::visit([&](auto const & expression){ return eval_impl(context, expression); }, *expression); @@ -784,6 +794,16 @@ namespace pslang::interpreter throw std::runtime_error("Not implemented"); } + value * eval_ref_impl(context & context, ast::sizeof_operator const & sizeof_operator) + { + throw std::runtime_error("Not implemented"); + } + + value * eval_ref_impl(context & context, ast::alignof_operator const & alignof_operator) + { + throw std::runtime_error("Not implemented"); + } + value * eval_ref_impl(context & context, ast::expression_ptr const & expression) { return std::visit([&](auto const & expression){ return eval_ref_impl(context, expression); }, *expression); diff --git a/libs/ir/source/compiler.cpp b/libs/ir/source/compiler.cpp index bde5aa7..8cbee69 100644 --- a/libs/ir/source/compiler.cpp +++ b/libs/ir/source/compiler.cpp @@ -513,6 +513,18 @@ namespace pslang::ir return result; } + node_ref apply(ast::sizeof_operator const & node) + { + mcontext.nodes->emplace_back(literal{ast::literal{ast::u64_literal{ast::type_size(*node.inferred_source_type)}}}, node.inferred_type); + return last(); + } + + node_ref apply(ast::alignof_operator const & node) + { + mcontext.nodes->emplace_back(literal{ast::literal{ast::u64_literal{ast::type_alignment(*node.inferred_source_type)}}}, node.inferred_type); + return last(); + } + // Statements node_ref apply(ast::expression_ptr const & node) diff --git a/libs/parser/rules/pslang.l b/libs/parser/rules/pslang.l index ad99a32..1f3108e 100644 --- a/libs/parser/rules/pslang.l +++ b/libs/parser/rules/pslang.l @@ -39,6 +39,8 @@ return { return bp::make_return(ctx.location); } struct { return bp::make_struct(ctx.location); } true { return bp::make_true(ctx.location); } false { return bp::make_false(ctx.location); } +sizeof { return bp::make_sizeof(ctx.location); } +alignof { return bp::make_alignof(ctx.location); } unit { return bp::make_unit(ctx.location); } bool { return bp::make_bool(ctx.location); } diff --git a/libs/parser/rules/pslang.y b/libs/parser/rules/pslang.y index 8c77327..b913f5b 100644 --- a/libs/parser/rules/pslang.y +++ b/libs/parser/rules/pslang.y @@ -150,6 +150,8 @@ template %token struct %token true %token false +%token sizeof +%token alignof %token unit %token bool @@ -355,6 +357,8 @@ expression | asterisk expression %prec DEREFERENCE { $$ = ast::unary_operation{ast::unary_operation_type::dereference, std::make_unique($2), @$ }; } | postfix_expression { $$ = $1; } | if expression then expression else expression { $$ = ast::if_expression{ std::make_unique($2), std::make_unique($4), std::make_unique($6), @$ }; } +| sizeof lparen type_expression rparen { $$ = ast::sizeof_operator{ nullptr, std::make_unique($3), @$ }; } +| alignof lparen type_expression rparen { $$ = ast::alignof_operator{ nullptr, std::make_unique($3), @$ }; } ; postfix_expression diff --git a/plans.txt b/plans.txt index 44cc6f8..32504e1 100644 --- a/plans.txt +++ b/plans.txt @@ -1,5 +1,6 @@ Future plans: * Globals (requires a separate mmaped segment in JIT compiler) +* Merge ast types and values (helps for sizeof/alignof builtins, also paves the path for metaprogramming) * Function overloading: separate functions from values (again) in interpreter, allow casting to specific function type to take function value * Const propagation: annotate expression AST nodes that are computable in compile-time * Generic parameters: can be either values or `t : type`, but always compile-time @@ -27,11 +28,11 @@ Optimizer plans: General backlog: * Mutually recursive structs (relevant only with pointers) * Empty array expression -* Calling functions as methods * Replace std::runtime_error with appropriate custom exception types * Replace std::ostringstream with std::format (need support for std::format in type/ast printing) * TEST COVERAGE!!! + Platform-dependent operations backlog: * Division by zero (returns 0 on arm64, triggers an interrupt on x86) - declare as UB? * Floating-point <-> integer conversion (is rounding the same on all platforms?) diff --git a/spec.txt b/spec.txt index 3f72969..4d9e079 100644 --- a/spec.txt +++ b/spec.txt @@ -139,6 +139,15 @@ Assignment: x = 15 // requires x to be a mut variable *p = 15 // p must be a pointer to mut +Special built-ins: + typeof(value) + sizeof(type) + sizeof(value) // same as sizeof(typeof(value)) + alignof(type) + alignof(value) // same as alignof(typeof(value)) + offsetof(struct type, field) + offsetof(field access expression) + ======== FLOW CONTROL ======== Flow control: