Skip to content

Commit

Permalink
Merge branch 'main' into last_use
Browse files Browse the repository at this point in the history
  • Loading branch information
JohelEGP committed Dec 21, 2023
2 parents 06bc83c + 210b2a0 commit 3ad7c88
Show file tree
Hide file tree
Showing 120 changed files with 1,697 additions and 595 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto
48 changes: 48 additions & 0 deletions .github/workflows/build-cppfront.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Multi-platform Build of cppfront
on: push
jobs:
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: ilammy/msvc-dev-cmd@v1
- name: Compiler name & version
run: cl.exe
- name: Build
run: cl.exe source/cppfront.cpp -std:c++latest -MD -EHsc -experimental:module -W4
build-unix-like:
strategy:
fail-fast: false
matrix:
runs-on: [ubuntu-latest]
compiler: [g++-10, g++-11, g++-12, clang++-12, clang++-14]
cxx-std: ['c++20', 'c++2b']
exclude:
# GCC 10 doesn't have support for c++23
- compiler: g++-10
cxx-std: 'c++2b'
# Clang 12 and 14 do not compile on 'c++2b' due to llvm/llvm-project#58206
- compiler: clang++-12
cxx-std: 'c++2b'
- compiler: clang++-14
cxx-std: 'c++2b'
include:
- runs-on: macos-11
compiler: clang++
cxx-std: 'c++20'
- runs-on: macos-latest
compiler: clang++
cxx-std: 'c++20'
runs-on: ${{ matrix.runs-on }}
env:
CXX: ${{ matrix.compiler }}
CXXFLAGS: -std=${{ matrix.cxx-std }} -Wall -Wextra -Wold-style-cast -pthread
steps:
- uses: actions/checkout@v3
- name: Install compiler
if: startsWith(matrix.runs-on, 'ubuntu')
run: sudo apt-get install -y $CXX
- name: Compiler name & version
run: $CXX --version
- name: Build
run: $CXX source/cppfront.cpp $CXXFLAGS -o cppfront
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
See [License](LICENSE)

[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)
[![Build (clang, gcc, vs)](https://github.com/hsutter/cppfront/actions/workflows/build-cppfront.yaml/badge.svg)](https://github.com/hsutter/cppfront/actions/workflows/build-cppfront.yaml)

Cppfront is an experimental compiler from a potential C++ 'syntax 2' (Cpp2) to today's 'syntax 1' (Cpp1), to learn some things, prove out some concepts, and share some ideas. This compiler is a work in progress and currently hilariously incomplete... basic functions work, classes will be next, then metaclasses and lightweight exceptions.

Expand Down
107 changes: 59 additions & 48 deletions include/cpp2util.h
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,11 @@ inline constexpr auto max(auto... values) {
template <class T, class... Ts>
inline constexpr auto is_any = std::disjunction_v<std::is_same<T, Ts>...>;

template <std::size_t Len, std::size_t Align>
struct aligned_storage {
alignas(Align) unsigned char data[Len];
};


//-----------------------------------------------------------------------
//
Expand Down Expand Up @@ -381,16 +386,24 @@ struct String
// Before C++23 std::string_view was not guaranteed to be trivially copyable,
// and so in<T> will pass it by const& and really it should be by value
#define CPP2_MESSAGE_PARAM char const*
#define CPP2_CONTRACT_MSG cpp2::message_to_cstr_adapter

auto message_to_cstr_adapter( CPP2_MESSAGE_PARAM msg ) -> CPP2_MESSAGE_PARAM { return msg ? msg : ""; }
auto message_to_cstr_adapter( std::string const& msg ) -> CPP2_MESSAGE_PARAM { return msg.c_str(); }

class contract_group {
public:
using handler = void (*)(CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM);

constexpr contract_group (handler h = {}) : reporter(h) { }
constexpr auto set_handler(handler h);
constexpr contract_group (handler h = {}) : reporter{h} { }
constexpr auto set_handler(handler h = {}) { reporter = h; }
constexpr auto get_handler() const -> handler { return reporter; }
constexpr auto expects (bool b, CPP2_MESSAGE_PARAM msg = "" CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT)
-> void { if (!b) reporter(msg CPP2_SOURCE_LOCATION_ARG); }
constexpr auto has_handler() const -> bool { return reporter != handler{}; }

constexpr auto enforce(bool b, CPP2_MESSAGE_PARAM msg = "" CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT)
-> void { if (!b) report_violation(msg CPP2_SOURCE_LOCATION_ARG); }
constexpr auto report_violation(CPP2_MESSAGE_PARAM msg = "" CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT)
-> void { if (reporter) reporter(msg CPP2_SOURCE_LOCATION_ARG); }
private:
handler reporter;
};
Expand All @@ -403,7 +416,7 @@ class contract_group {
<< where.function_name() << ": "
#endif
<< group << " violation";
if (msg[0] != '\0') {
if (msg && msg[0] != '\0') {
std::cerr << ": " << msg;
}
std::cerr << "\n";
Expand Down Expand Up @@ -436,11 +449,6 @@ auto inline Testing = contract_group(
}
);

constexpr auto contract_group::set_handler(handler h) {
Default.expects(h);
reporter = h;
}


// Null pointer deref checking
//
Expand All @@ -450,7 +458,9 @@ auto assert_not_null(auto&& p CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> declty
// doesn't guarantee that using == and != will reliably report whether an
// STL iterator has the default-constructed value. So use it only for raw *...
if constexpr (std::is_pointer_v<CPP2_TYPEOF(p)>) {
Null.expects(p != CPP2_TYPEOF(p){}, "dynamic null dereference attempt detected" CPP2_SOURCE_LOCATION_ARG);
if (p == CPP2_TYPEOF(p){}) {
Null.report_violation("dynamic null dereference attempt detected" CPP2_SOURCE_LOCATION_ARG);
};
}
return CPP2_FORWARD(p);
}
Expand All @@ -465,7 +475,16 @@ auto assert_in_bounds_impl(auto&& x, auto&& arg CPP2_SOURCE_LOCATION_PARAM_WITH_
if constexpr (std::is_signed_v<CPP2_TYPEOF(arg)>) { return std::ssize(x); }
else { return std::size(x); }
};
Bounds.expects(0 <= arg && arg < max(), ("out of bounds access attempt detected - attempted index " + std::to_string(arg) + ", [min,max] range is [0," + std::to_string(max()-1) + "]").c_str() CPP2_SOURCE_LOCATION_ARG);
auto msg = "out of bounds access attempt detected - attempted access at index " + std::to_string(arg) + ", ";
if (max() > 0 ) {
msg += "[min,max] range is [0," + std::to_string(max()-1) + "]";
}
else {
msg += "but container is empty";
}
if (!(0 <= arg && arg < max())) {
Bounds.report_violation(msg.c_str() CPP2_SOURCE_LOCATION_ARG);
}
}

auto assert_in_bounds_impl(auto&&, auto&& CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> void
Expand All @@ -492,10 +511,11 @@ auto assert_in_bounds_impl(auto&&, auto&& CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAUL

[[noreturn]] auto Throw(auto&& x, [[maybe_unused]] char const* msg) -> void {
#ifdef CPP2_NO_EXCEPTIONS
Type.expects(
!"exceptions are disabled with -fno-exceptions",
msg
);
auto err = std::string{"exceptions are disabled with -fno-exceptions - attempted to throw exception with type \"" + typeid(decltype(x)).name() + "\""};
if (msg) {
err += " and the message \"" + msg + "\"";
}
Type.report_violation( err );
std::terminate();
#else
throw CPP2_FORWARD(x);
Expand All @@ -513,10 +533,7 @@ inline auto Uncaught_exceptions() -> int {
template<typename T>
auto Dynamic_cast( [[maybe_unused]] auto&& x ) -> decltype(auto) {
#ifdef CPP2_NO_RTTI
Type.expects(
!"'as' dynamic casting is disabled with -fno-rtti", // more likely to appear on console
"'as' dynamic casting is disabled with -fno-rtti" // make message available to hooked handlers
);
Type.report_violation( "'as' dynamic casting is disabled with -fno-rtti" );
return nullptr;
#else
return dynamic_cast<T>(CPP2_FORWARD(x));
Expand All @@ -526,25 +543,19 @@ auto Dynamic_cast( [[maybe_unused]] auto&& x ) -> decltype(auto) {
template<typename T>
auto Typeid() -> decltype(auto) {
#ifdef CPP2_NO_RTTI
Type.expects(
!"'any' dynamic casting is disabled with -fno-rtti", // more likely to appear on console
"'any' dynamic casting is disabled with -fno-rtti" // make message available to hooked handlers
);
Type.report_violation( "'any' dynamic casting is disabled with -fno-rtti" );
#else
return typeid(T);
#endif
}

// We don't need typeid(expr) yet -- uncomment this if/when we need it
//auto Typeid( [[maybe_unused]] auto&& x ) -> decltype(auto) {
//#ifdef CPP2_NO_RTTI
// Type.expects(
// !"<write appropriate error message here>"
// );
//#else
// return typeid(CPP2_FORWARD(x));
//#endif
//}
auto Typeid( [[maybe_unused]] auto&& x ) -> decltype(auto) {
#ifdef CPP2_NO_RTTI
Type.report_violation( "'typeid' is disabled with -fno-rtti" );
#else
return typeid(CPP2_FORWARD(x));
#endif
}


//-----------------------------------------------------------------------
Expand Down Expand Up @@ -656,9 +667,9 @@ class deferred_init {
public:
deferred_init() noexcept { }
~deferred_init() noexcept { destroy(); }
auto value() noexcept -> T& { Default.expects(init); return t(); }
auto value() noexcept -> T& { Default.enforce(init); return t(); }

auto construct(auto&& ...args) -> void { Default.expects(!init); new (&data) T{CPP2_FORWARD(args)...}; init = true; }
auto construct(auto&& ...args) -> void { Default.enforce(!init); new (&data) T{CPP2_FORWARD(args)...}; init = true; }
};


Expand All @@ -678,9 +689,9 @@ class out {
bool called_construct_ = false;

public:
out(T* t_) noexcept : t{ t_}, has_t{true} { Default.expects( t); }
out(deferred_init<T>* dt_) noexcept : dt{dt_}, has_t{false} { Default.expects(dt); }
out(out<T>* ot_) noexcept : ot{ot_}, has_t{ot_->has_t} { Default.expects(ot);
out(T* t_) noexcept : t{ t_}, has_t{true} { Default.enforce( t); }
out(deferred_init<T>* dt_) noexcept : dt{dt_}, has_t{false} { Default.enforce(dt); }
out(out<T>* ot_) noexcept : ot{ot_}, has_t{ot_->has_t} { Default.enforce(ot);
if (has_t) { t = ot->t; }
else { dt = ot->dt; }
}
Expand All @@ -694,7 +705,7 @@ class out {
// then leave it in the same state on exit (strong guarantee)
~out() {
if (called_construct() && uncaught_count != Uncaught_exceptions()) {
Default.expects(!has_t);
Default.enforce(!has_t);
dt->destroy();
called_construct() = false;
}
Expand All @@ -703,21 +714,21 @@ class out {
auto construct(auto&& ...args) -> void {
if (has_t || called_construct()) {
if constexpr (requires { *t = T(CPP2_FORWARD(args)...); }) {
Default.expects( t );
Default.enforce( t );
*t = T(CPP2_FORWARD(args)...);
}
else {
Default.expects(false, "attempted to copy assign, but copy assignment is not available");
Default.report_violation("attempted to copy assign, but copy assignment is not available");
}
}
else {
Default.expects( dt );
Default.enforce( dt );
if (dt->init) {
if constexpr (requires { *t = T(CPP2_FORWARD(args)...); }) {
dt->value() = T(CPP2_FORWARD(args)...);
}
else {
Default.expects(false, "attempted to copy assign, but copy assignment is not available");
Default.report_violation("attempted to copy assign, but copy assignment is not available");
}
}
else {
Expand All @@ -729,11 +740,11 @@ class out {

auto value() noexcept -> T& {
if (has_t) {
Default.expects( t );
Default.enforce( t );
return *t;
}
else {
Default.expects( dt );
Default.enforce( dt );
return dt->value();
}
}
Expand Down Expand Up @@ -1186,7 +1197,7 @@ inline constexpr auto as(auto const& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT)
)
{
const C c = static_cast<C>(x);
Type.expects( // precondition check: must be round-trippable => not lossy
Type.enforce( // precondition check: must be round-trippable => not lossy
static_cast<CPP2_TYPEOF(x)>(c) == x && (c < C{}) == (x < CPP2_TYPEOF(x){}),
"dynamic lossy narrowing conversion attempt detected" CPP2_SOURCE_LOCATION_ARG
);
Expand Down Expand Up @@ -1685,7 +1696,7 @@ struct args_t : std::vector<std::string_view>
{
args_t(int c, char** v) : vector{static_cast<std::size_t>(c)}, argc{c}, argv{v} {}

int argc = 0;
mutable int argc = 0; // mutable for compatibility with frameworks that take 'int& argc'
char** argv = nullptr;
};

Expand Down
55 changes: 55 additions & 0 deletions regression-tests/mixed-increment-decrement.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

iterator: @value <T> type = {
public x: T = 0;
operator++: (inout this) -> forward iterator<T> requires true = { x++; return this; }
}

iterator2: @value <T> type = {
public x: T = 0;
}
operator++: <T> (inout it: iterator2<T>) -> forward iterator2<T> requires true = { it.x++; return it; }

int main() {
{
iterator<int> i;
std::cout << (++i).x;
std::cout << (++i).x;
std::cout << i++.x;
std::cout << i++.x;
std::cout << i.x << "\n";
}

{
iterator2<int> i;
std::cout << (++i).x;
std::cout << (++i).x;
std::cout << i++.x;
std::cout << i++.x;
std::cout << i.x << "\n";
}
}



number: @struct type = {
operator<=>: (this, that) -> _;
operator-: (this, _) -> int = 0;
}

number_line: @struct <Op: i8, Id: i32> type = {
this: number;
}

number_line_pre_increment: <Id: i32> type == number_line<0b1110, Id>;
number_line_pre_decrement: <Id: i32> type == number_line<0b1101, Id>;
number_line_post_increment: <Id: i32> type == number_line<0b1011, Id>;
number_line_post_decrement: <Id: i32> type == number_line<0b0111, Id>;

operator++: <Op: i8, Id: i32> (inout x: number_line<Op, Id>) -> forward number_line<Op, Id> requires bool(Op & 1) = x;
operator--: <Op: i8, Id: i32> (inout x: number_line<Op, Id>) -> forward number_line<Op, Id> requires bool(Op & 2) = x;

operator++: (inout x: number_line_pre_increment<0>) -> forward number_line_pre_increment<0> = x;
operator--: (inout x: number_line_pre_decrement<0>) -> forward number_line_pre_decrement<0> = x;

operator++: (inout x: number_line_pre_increment<1>) -> forward const number_line_pre_increment<1> = x;
operator--: (inout x: number_line_pre_decrement<1>) -> forward const number_line_pre_decrement<1> = x;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
t: type = {
operator[]: (this, _) = { }
}

main: () -> int = {
(x := t()) { x[:() -> _ = 0]; }
(x := t()) { x[:() -> _ = 0;]; }

assert(!(:() 0; is int));

return :i32 = 0;
}

x :== :i32 = 0;
y: i32 = 0;
Loading

0 comments on commit 3ad7c88

Please sign in to comment.