Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions antlr4/DepCLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ KW_I32_T: 'i32_t';
KW_I64_T: 'i64_t';
KW_I8_T: 'i8_t';
KW_IF: 'if';
KW_IMMUTABLE: 'immutable';
KW_IMPOSSIBLE: 'impossible';
KW_MUTABLE: 'mutable';
KW_NOT: 'not';
Expand Down
6 changes: 4 additions & 2 deletions antlr4/DepCParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ structDef: 'struct' name=ID '{' (fieldDecl SEMI)* '}' SEMI;
fieldDecl: fieldType=expr fieldName=ID;

// Types
funcArg: ({one_of("0", "1")}? qty=INT)? ('typename' | expr) name=ID?;
funcArg: ({one_of("0", "1")}? qty=INT)? ('typename' | expr) 'mutable'? name=ID?;
type: primitiveType | funcType | tupleType | typeVar;
primitiveType: 'bool_t' | 'cstr_t' | 'unit_t' | 'i8_t' | 'i16_t' | 'i32_t' | 'i64_t' | 'u8_t' | 'u16_t' | 'u32_t' | 'u64_t';
funcType: '(' (funcArg (',' funcArg)*)? ')' 'mutable'? '->' ('typename' | retType=expr);
Expand All @@ -65,9 +65,11 @@ typeVar: name=ID;
// Statements
body: '{' stmt* '}';

stmt: funcCallStmt | ifElse | returnStmt | impossibleStmt;
stmt: funcCallStmt | assignment | immutableBlock | ifElse | returnStmt | impossibleStmt;

funcCallStmt: func=expr '(' (expr (',' expr)*)? ')' ';';
assignment: lhs=expr '=' rhs=expr ';';
immutableBlock: 'immutable' '(' ID (',' ID)* ')' body;
ifElse: 'if' '(' cond=expr ')' true_branch=bodyOrStmt ('else' false_branch=bodyOrStmt)?;
bodyOrStmt: body | stmt;
returnStmt: 'return' expr? ';';
Expand Down
6 changes: 2 additions & 4 deletions dep0/lib/00_core/include/dep0/match.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,9 @@ template <typename... Ts, typename... Fs>
decltype(auto) match(std::variant<Ts...> const& x, Fs&&... fs)
{
using F = decltype(boost::hana::overload(std::forward<Fs>(fs)...));
using R = decltype(std::declval<F>()(std::get<0>(x)));
auto const jump_table = [&] <std::size_t... Is> (std::index_sequence<Is...>)
{
static_assert((std::is_same_v<R, decltype(std::declval<F>()(std::get<Is>(x)))> or ...));
using R = std::common_reference_t<decltype(std::declval<F>()(std::get<Is>(x)))...>;
return std::array{
(+[] (std::variant<Ts...> const& x, F&& f) -> R
{
Expand All @@ -52,10 +51,9 @@ template <typename... Ts, typename... Fs>
decltype(auto) match(std::variant<Ts...>& x, Fs&&... fs)
{
using F = decltype(boost::hana::overload(std::forward<Fs>(fs)...));
using R = decltype(std::declval<F>()(std::get<0>(x)));
auto const jump_table = [&] <std::size_t... Is> (std::index_sequence<Is...>)
{
static_assert((std::is_same_v<R, decltype(std::declval<F>()(std::get<Is>(x)))> or ...));
using R = std::common_reference_t<decltype(std::declval<F>()(std::get<Is>(x)))...>;
return std::array{
(+[] (std::variant<Ts...>& x, F&& f) -> R
{
Expand Down
10 changes: 10 additions & 0 deletions dep0/lib/00_core/test/dep0_core_match_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ BOOST_AUTO_TEST_CASE(three_match_three_out_of_order)
BOOST_TEST(dep0::match(z, f3, f1, f2) == "test");
}

BOOST_AUTO_TEST_CASE(deduce_common_return_type)
{
std::variant<int, dummy_t, std::string> x{23};
auto const f1 = [] (int) { return nullptr; };
auto const f2 = [] (dummy_t) { return ""; };
auto const f3 = [] (std::string const& x) { return x.c_str(); };
static_assert(std::is_same_v<char const*, decltype(dep0::match(x, f1, f2, f3))>);
static_assert(std::is_same_v<char const*, decltype(dep0::match(std::as_const(x), f1, f2, f3))>);
}

BOOST_AUTO_TEST_CASE(return_const_ref)
{
std::variant<int, dummy_t> x{dummy_t{23}};
Expand Down
2 changes: 2 additions & 0 deletions dep0/lib/01_ast/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ add_library(dep0_ast_lib
include/dep0/ast/max_index.hpp
include/dep0/ast/max_index_impl.hpp
include/dep0/ast/mutable.hpp
include/dep0/ast/mutable_place_expression.hpp
include/dep0/ast/occurs_in.hpp
include/dep0/ast/occurs_in_impl.hpp
include/dep0/ast/place_expression.hpp
Expand All @@ -40,6 +41,7 @@ add_library(dep0_ast_lib
src/hash_code.cpp
src/max_index.cpp
src/mutable.cpp
src/mutable_place_expression.cpp
src/occurs_in.cpp
src/place_expression.cpp
src/pretty_print.cpp
Expand Down
15 changes: 15 additions & 0 deletions dep0/lib/01_ast/include/dep0/ast/alpha_equivalence_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,21 @@ struct alpha_equivalence_visitor
return is_alpha_equivalent_impl(x.value.get(), y.value.get());
}

result_t operator()(typename stmt_t<P>::assign_t& x, typename stmt_t<P>::assign_t& y) const
{
auto eq = is_alpha_equivalent_impl(x.lhs, y.lhs);
if (eq)
eq = is_alpha_equivalent_impl(x.rhs, y.rhs);
return eq;
}

result_t operator()(typename stmt_t<P>::immutable_t& x, typename stmt_t<P>::immutable_t& y) const
{
if (x.vars != y.vars)
return dep0::error_t("immutable blocks must have the same set of variables");
return is_alpha_equivalent_impl(x.body, y.body);
}

result_t operator()(typename stmt_t<P>::if_else_t& x, typename stmt_t<P>::if_else_t& y) const
{
auto eq = is_alpha_equivalent_impl(x.cond, y.cond);
Expand Down
18 changes: 17 additions & 1 deletion dep0/lib/01_ast/include/dep0/ast/ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <boost/variant/recursive_wrapper.hpp>

#include <optional>
#include <set>
#include <tuple>
#include <vector>
#include <variant>
Expand Down Expand Up @@ -424,6 +425,7 @@ struct func_arg_t

properties_t properties;
qty_t qty;
is_mutable_t is_mutable;
expr_t type;
std::optional<typename expr_t::var_t> var;
};
Expand All @@ -440,6 +442,20 @@ struct stmt_t
using body_t = ast::body_t<P>;
using expr_t = ast::expr_t<P>;

/** @brief Represents an assignment `lhs = rhs;`, for example `x = x+1;` or `x.values[0] = f(23);` */
struct assign_t
{
expr_t lhs;
expr_t rhs;
};

/** @brief Represents an immutable block, for example `immutable(x, y) { ... }`. */
struct immutable_t
{
std::set<typename expr_t::var_t> vars;
body_t body;
};

/** @brief Represents an `if` or `if-else` statement, whose condition must be of type `%bool_t`. */
struct if_else_t
{
Expand Down Expand Up @@ -473,7 +489,7 @@ struct stmt_t
std::optional<expr_t> reason;
};

using value_t = std::variant<typename expr_t::app_t, if_else_t, return_t, impossible_t>;
using value_t = std::variant<typename expr_t::app_t, assign_t, immutable_t, if_else_t, return_t, impossible_t>;

properties_t properties;
value_t value;
Expand Down
11 changes: 11 additions & 0 deletions dep0/lib/01_ast/include/dep0/ast/hash_code_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ std::size_t hash_code_impl(hash_code_state_t<P>& state, stmt_t<P> const& x)
{
return hash_code_impl<P>(state, x);
},
[&] (stmt_t<P>::assign_t const& assign)
{
return combine(hash_code_impl(state, assign.lhs), hash_code_impl(state, assign.rhs));
},
[&] (stmt_t<P>::immutable_t const& immutable)
{
std::size_t result = 0ul;
for (auto const& var: immutable.vars)
boost::hash_combine(result, hash_code_impl(state, var));
return combine(result, hash_code_impl(state, immutable.body));
},
[&] (stmt_t<P>::if_else_t const& if_)
{
return combine(
Expand Down
43 changes: 27 additions & 16 deletions dep0/lib/01_ast/include/dep0/ast/max_index_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ template <Properties P> std::size_t max_index(body_t<P> const&);
template <Properties P> std::size_t max_index(expr_t<P> const&);
template <Properties P> std::size_t max_index(typename expr_t<P>::app_t const&);

} // namespace impl

namespace {
template <Properties P>
inline constexpr auto max_accumulator =
boost::hana::overload(
[] (std::size_t const x, func_arg_t<P> const& y) { return std::max(x, impl::max_index(y)); },
[] (std::size_t const x, typename expr_t<P>::var_t const& y) { return std::max(x, y.idx); },
[] (std::size_t const x, typename type_def_t<P>::struct_t::field_t const& y)
{
return std::max(x, std::max(impl::max_index(y.type), y.var.idx));
});
} // namespace

namespace impl {

template <Properties P>
std::size_t max_index(func_arg_t<P> const& x)
{
Expand All @@ -47,6 +63,14 @@ std::size_t max_index(body_t<P> const& x)
{
return max_index<P>(x);
},
[] (stmt_t<P>::assign_t const& x)
{
return std::max(max_index(x.lhs), max_index(x.rhs));
},
[] (stmt_t<P>::immutable_t const& x)
{
return std::accumulate(x.vars.begin(), x.vars.end(), max_index(x.body), max_accumulator<P>);
},
[] (stmt_t<P>::if_else_t const& if_)
{
return std::max(
Expand Down Expand Up @@ -190,13 +214,6 @@ std::size_t max_index(typename expr_t<P>::app_t const& x)

} // namespace impl

namespace {
inline constexpr auto max_accumulator = [] <Properties P> (std::size_t const acc, func_arg_t<P> const& arg)
{
return std::max(acc, impl::max_index(arg));
};
}

template <Properties P>
std::size_t max_index(
typename std::vector<func_arg_t<P>>::const_iterator const begin,
Expand All @@ -205,29 +222,23 @@ std::size_t max_index(
body_t<P> const* body)
{
auto const initial_value = std::max(impl::max_index(ret_type), body ? impl::max_index(*body) : 0ul);
return std::accumulate(begin, end, initial_value, max_accumulator);
return std::accumulate(begin, end, initial_value, max_accumulator<P>);
}

template <Properties P>
std::size_t max_index(
typename std::vector<func_arg_t<P>>::const_iterator const begin,
typename std::vector<func_arg_t<P>>::const_iterator const end)
{
return std::accumulate(begin, end, 0ul, max_accumulator);
return std::accumulate(begin, end, 0ul, max_accumulator<P>);
}

template <Properties P>
std::size_t max_index(
typename std::vector<typename type_def_t<P>::struct_t::field_t>::const_iterator const begin,
typename std::vector<typename type_def_t<P>::struct_t::field_t>::const_iterator const end)
{
return std::accumulate(
begin, end,
0ul,
[] (std::size_t const acc, type_def_t<P>::struct_t::field_t const& field)
{
return std::max(acc, std::max(impl::max_index(field.type), field.var.idx));
});
return std::accumulate(begin, end, 0ul, max_accumulator<P>);
}

} // namespace dep0::ast
55 changes: 55 additions & 0 deletions dep0/lib/01_ast/include/dep0/ast/mutable_place_expression.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright Raffaele Rossi 2025.
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
*/
/**
* @file
* @brief Single-function header declaring `dep0::ast::is_mutable_place_expression()`.
*/
#pragma once

#include "dep0/ast/ast.hpp"
#include "dep0/ast/unwrap_because.hpp"

#include "dep0/match.hpp"

#include <variant>

namespace dep0::ast {

/** @brief The expression passed to `is_mutable_place_expression()` is not a mutable place. */
struct immutable_place_t { };

template <Properties P>
using is_mutable_place_expression_result_t =
std::variant<
immutable_place_t,
std::reference_wrapper<typename expr_t<P>::var_t const>,
std::reference_wrapper<typename expr_t<P>::member_t const>,
std::reference_wrapper<typename expr_t<P>::subscript_t const>>;

/**
* @brief Decide whether or not the given expression is a mutable place expression,
* possibly by looking inside because-expressions, eg `x because reason`.
*
* A mutable place expression is an expression that yields a **possibly** mutable memory location,
* for example the name of a variable or the element of an array.
* Whether or not such place is really mutable depends on whether the root variable was declared mutable.
*
* @remarks This concept is fundamentally the same as a place expression but without dereferences.
*/
template <Properties P>
is_mutable_place_expression_result_t<P> is_mutable_place_expression(expr_t<P> const& expr)
{
using result_t = is_mutable_place_expression_result_t<P>;
return match(
unwrap_because(expr).value,
[] (expr_t<P>::var_t const& x) { return result_t{std::cref(x)}; },
[] (expr_t<P>::member_t const& x) { return result_t{std::cref(x)}; },
[] (expr_t<P>::subscript_t const& x) { return result_t{std::cref(x)}; },
[] (auto const&) { return result_t{immutable_place_t{}}; });
}

} // namespace dep0::ast
8 changes: 8 additions & 0 deletions dep0/lib/01_ast/include/dep0/ast/occurs_in.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ bool occurs_in(
body_t<P> const* body,
occurrence_style);

/** @brief Returns true if the given variable name appears (free or anywhere) in any of the given struct fields. */
template <Properties P>
bool occurs_in(
typename expr_t<P>::var_t const&,
typename std::vector<typename type_def_t<P>::struct_t::field_t>::const_iterator begin,
typename std::vector<typename type_def_t<P>::struct_t::field_t>::const_iterator end,
occurrence_style);

} // namespace dep0::ast

#include "dep0/ast/occurs_in_impl.hpp"
28 changes: 27 additions & 1 deletion dep0/lib/01_ast/include/dep0/ast/occurs_in_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ bool occurs_in(typename expr_t<P>::var_t const& var, body_t<P> const& x, occurre
{
return occurs_in<P>(var, app, style);
},
[&] (stmt_t<P>::assign_t const& assign)
{
return occurs_in(var, assign.lhs, style) or occurs_in(var, assign.rhs, style);
},
[&] (stmt_t<P>::immutable_t const& immutable)
{
return immutable.vars.contains(var) or occurs_in(var, immutable.body, style);
},
[&] (stmt_t<P>::if_else_t const& if_)
{
return occurs_in(var, if_.cond, style)
Expand Down Expand Up @@ -214,5 +222,23 @@ bool occurs_in(
return occurs_in(var, ret_type, style) or (body and impl::occurs_in(var, *body, style));
}

} // namespace dep0::ast
template <Properties P>
bool occurs_in(
typename expr_t<P>::var_t const& var,
typename std::vector<typename type_def_t<P>::struct_t::field_t>::const_iterator const begin,
typename std::vector<typename type_def_t<P>::struct_t::field_t>::const_iterator const end,
occurrence_style const style)
{
for (auto const& arg: std::ranges::subrange(begin, end))
{
if (occurs_in(var, arg.type, style))
return true;
if (arg.var == var)
// If we are looking for occurrences anywhere, return true because this is a valid one.
// If we are looking for free occurrences, return false because any later occurrence is now bound.
return style == occurrence_style::anywhere;
}
return false;
}

} // namespace dep0::ast
6 changes: 6 additions & 0 deletions dep0/lib/01_ast/include/dep0/ast/pretty_print.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ std::ostream& pretty_print(std::ostream&, body_t<P> const&, std::size_t indent =
template <Properties P>
std::ostream& pretty_print(std::ostream&, stmt_t<P> const&, std::size_t indent = 0ul);

template <Properties P>
std::ostream& pretty_print(std::ostream&, typename stmt_t<P>::assign_t const&, std::size_t indent = 0ul);

template <Properties P>
std::ostream& pretty_print(std::ostream&, typename stmt_t<P>::immutable_t const&, std::size_t indent = 0ul);

template <Properties P>
std::ostream& pretty_print(std::ostream&, typename stmt_t<P>::if_else_t const&, std::size_t indent = 0ul);

Expand Down
Loading