Skip to content

Commit

Permalink
New Circle drop
Browse files Browse the repository at this point in the history
  • Loading branch information
seanbaxter committed Jan 22, 2023
1 parent 788356f commit cd631a0
Show file tree
Hide file tree
Showing 84 changed files with 6,944 additions and 3 deletions.
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
# Circle
The C++ Automation Language
2021
2023
Sean Baxter

Download [here](https://www.circle-lang.org/)

Follow me on Twitter [@seanbax](https://www.twitter.com/seanbax) for compiler updates.

## New Circle

* [New Circle notes](new-circle/README.md)

New Circle is a major transformation of the Circle compiler, intended as a response to recent [successor language announcements](https://accu.org/journals/overload/30/172/teodorescu/). It focuses on a novel [fine-grained versioning mechanism](new-circle/README.md#versioning-with-feature-pragmas) that allows the language to **fix defects** and make the language **safer** and **more productive** while maintaining **100% compatibility** with existing code assets.

New Circle is the richest C++ compiler yet. Try out:
* [choice types](new-circle/README.md#choice),
* [pattern matching](new-circle/README.md#pattern-matching),
* [interfaces and impls](new-circle/README.md#interface),
* [language type erasure](new-circle/README.md#language-type-erasure),
* [_as-expressions_](new-circle/README.md#as) for safer conversions,
* [a modern declaration syntax](new-circle/README.md#new_decl_syntax) with `fn` and `var` keywords to make clearer, less ambiguous declarations,
* [a simpler syntax for binary expressions](new-circle/README.md#simpler_precedence), greatly reducing the liklihood of bugs caused by confusing operator precedences,
* [a `forward` keyword](new-circle/README.md#forward) to take the complexity and bugginess out of forwarding references,
* [safer initializer lists](new-circle/README.md#safer_initializer_list), which address ambiguities when calling std::initializer_list constructors and non-std::initializer_list constructors,
* [lifting lambdas](new-circle/README.md#overload-sets-as-arguments) to pass overload sets as function arguments,
* [nine kinds of template parameters](new-circle/README.md#template-parameter-kinds) to make templates far more comprehensive,
* [reflection traits](new-circle/README.md#reflection-traits) to access packs of information about class types, enum types, function types, class specializations, and so on,
* [pack traits](new-circle/README.md#pack-traits) for pack-transforming algorithms, like sort, unique, count, erase, difference, intersection, and so on.

New Circle describes a path for evolving C++ to meet the needs of institutional users. The versioning mechanism that accommodated the development of the features above will also accommodate research into critically important areas like memory safety. Rather than insisting on a one-size-fit's-all approach to language development, project leads can opt into collections of features that best target their projects' needs.

## Old docs. May be out of date. Refer to [New Circle](new-circle/README.md) for fresh information.

* [Circle implementations of Standard Library classes](stdlib#circle-implementations-of-standard-library-classes)
* [Member packs and `std::tuple`](tuple#circle-tuple)
* [Member packs and `std::variant`](variant#circle-variant)
Expand Down
4,641 changes: 4,641 additions & 0 deletions new-circle/README.md

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions new-circle/as.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#pragma feature as // Permit any implicit conversion with "x as _".
void f_short(short x);
void f_int(int x);
void f_unsigned(unsigned x);
void f_long(long x);
void f_float(float x);
void f_double(double x);
void f_bool(bool x);

int main() {
#pragma feature no_implicit_integral_narrowing
int x_int = 100;
f_short(x_int); // Error
f_short(x_int as _); // OK
#pragma feature_off no_implicit_integral_narrowing

#pragma feature no_implicit_floating_narrowing
double x_double = 100;
f_float(x_double); // Error
f_float(x_double as _); // Ok
#pragma feature_off no_implicit_floating_narrowing

#pragma feature no_implicit_signed_to_unsigned
f_unsigned(x_int); // Error
f_unsigned(x_int as _); // OK
f_unsigned(x_double); // Error
f_unsigned(x_double as _); // OK
#pragma feature_off no_implicit_signed_to_unsigned

#pragma feature no_implicit_widening
char x_char = 'x';
f_short(x_char); // Error
f_short(x_char as _); // OK
f_long(x_int); // Error
f_long(x_int as _); // OK
float x_float = 1;
f_double(x_float); // Error
f_double(x_float as _); // OK
#pragma feature_off no_implicit_widening

#pragma feature as no_implicit_enum_to_underlying
enum numbers_t : int {
Zero, One, Two, Three,
};

f_int(Zero); // Error
f_int(Zero as _); // OK

f_int(numbers_t::Zero); // Error
f_int(numbers_t::Zero as _); // OK
#pragma feature_off no_implicit_enum_to_underlying

// Use as _ to allow implicit narrowing conversions inside
// braced-initializer.
short s1 { x_int }; // Error
short s2 { x_int as _ }; // OK
f_short({ x_int }); // Error
f_short({ x_int as _}); // OK
#pragma feature_off no_implicit_enum_to_underlying

// Implicit conversions from pointers to bools are permitted by C++.
const char* p = nullptr;
f_bool(p); // OK
#pragma feature no_implicit_pointer_to_bool
// They are disabled by [no_implicit_pointer_to_bool]
f_bool(p); // Error
f_bool(p as bool); // OK
f_bool(p as _); // OK
};
21 changes: 21 additions & 0 deletions new-circle/backtick.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <string>
#include <iostream>

struct Data {
// Strings in tickmarks
std::string `First Name`;
std::string `Last Name`;
int single, `double`, triple;
};

int main() {
Data data { };
data.`First Name` = "Abe";
data.`Last Name` = "Lincoln";
data.single = 1;
data.`double` = 2;
data.triple = 3;

// Use reflection to print the name of each member and its value.
std::cout<< Data~member_names + ": "<< data~member_values<< "\n" ...;
}
4 changes: 4 additions & 0 deletions new-circle/build_all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
set -x
circle as.cxx
circle ctor_conversions.cxx
circle user_conversions.cxx
40 changes: 40 additions & 0 deletions new-circle/carbon1.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#pragma feature edition_carbon_2023
#include <string>
#include <iostream>

using String = std::string;

choice IntResult {
Success(int32_t),
Failure(String),
Cancelled,
}

fn ParseAsInt(s: String) -> IntResult {
var r : int32_t = 0;
for(var c in s) {
if(not isdigit(c)) {
return .Failure("Invalid character");
}

// Accumulate the digits as they come in.
r = 10 * r + c - '0';
}

return .Success(r);
}

fn TryIt(s: String) {
var result := ParseAsInt(s);
match(result) {
.Success(var x) => std::cout<< "Read integer "<< x<< "\n";
.Failure(var err) => std::cout<< "Failure '"<< err<< "'\n";
.Cancelled => std::terminate();
};
}

fn main() -> int {
TryIt("12345");
TryIt("12x45");
return 0;
}
Binary file added new-circle/carbon_proposals.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions new-circle/choice1.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma feature choice
#include <iostream>

choice IntResult {
Success(int),
Failure(std::string),
Cancelled,
};

template<typename T>
void func(T obj) {
match(obj) {
.Success(auto x) => std::cout<< "Success: "<< x<< "\n";
.Failure(auto x) => std::cout<< "Failure: "<< x<< "\n";
.Cancelled => std::terminate();
};
}

int main() {
IntResult r1 = .Success(12345);
IntResult r2 = .Failure("Hello");
IntResult r3 = .Cancelled();
func(r1);
func(r2);
func(r3);
}
31 changes: 31 additions & 0 deletions new-circle/choice2.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma feature choice new_decl_syntax
#include <string>
#include <tuple>
#include <iostream>

choice MyChoice {
MyTuple(int, std::string), // The payload is a tuple.
MyArray(double[3]), // The payload is an array.
MyScalar(short), // The payload is a scalar.
}

fn test(obj : MyChoice) {
// You can pattern match on tuples and arrays.
match(obj) {
.MyTuple([1, var b]) => std::cout<< "The tuple int is 1\n";
.MyArray([var a, a, a]) => std::cout<< "All array elements are "<< a<< "\n";
.MyArray(var [a, b, c]) => std::cout<< "Some other array\n";
.MyScalar(> 10) => std::cout<< "A scalar greater than 10\n";
.MyScalar(var x) => std::cout<< "The scalar is "<< x<< "\n";
_ => std::cout<< "Something else\n";
};
}

fn main() -> int {
test(.MyTuple{1, "Hello choice tuple"});
test(.MyArray{10, 20, 30});
test(.MyArray{50, 50, 50});
test(.MyScalar{100});
test(.MyScalar{6});
test(.MyTuple{2, "Foo"});
}
25 changes: 25 additions & 0 deletions new-circle/choice3.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma feature choice new_decl_syntax
#include <iostream>
#include <concepts>

fn even(x : std::integral auto) noexcept -> bool {
return 0 == x % 2;
}

fn func(arg : auto) {
match(arg) {
5 => std::cout<< "The arg is five.\n";
10 ... 20 => std::cout<< "The arg is between 10 and 20.\n";
even => std::cout<< "The arg is even.\n";
1 || 3 || 7 || 9 => std::cout<< "The arg is special.\n";
_ => std::cout<< "The arg is not special.\n";
};
}

fn main() -> int {
func(5);
func(13);
func(32);
func(7);
func(21);
}
32 changes: 32 additions & 0 deletions new-circle/choice4.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma feature choice tuple new_decl_syntax
#include <tuple>
#include <iostream>

struct obj_t {
var a : (int, int); // A 2-tuple.
var b : (std::string, double, int); // A 3-tuple.
}

fn func(arg : auto) {
match(arg) {
// Destructure the a member and test if it's (10, 20)
[a: (10, 20)] => std::cout<< "The 'a' member is (10, 20).\n";

// Check the range of the double tuple element.
[_, [_, 1...100, _] ] => std::cout<< "The double is between 1 and 100\n";

// a's 2nd element matches b's third element.
[ [_, var x], [_, _, x] ] => std::cout<< "A magical coincidence.\n";

// Everything else goes here.
_ => std::cout<< "A rubbish struct.\n";
};
}

fn main() -> int {
func(obj_t { { 10, 20 }, { "Hello", 3, 4 } });
func(obj_t { { 2, 4 }, { "Hello", 3, 4 } });
func(obj_t { { 2, 5 }, { "Hello", 19.0, 4 } });
func(obj_t { { 2, 5 }, { "Hello", 101.0, 5 } });
func(obj_t { { 2, 5 }, { "Hello", 101.0, 6 } });
}
54 changes: 54 additions & 0 deletions new-circle/choice5.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#pragma feature choice
#include <type_traits>
#include <iostream>

choice Foo {
x(int),
y(double),
z(const char*),
};

template<typename T> requires (T~is_enum)
const char* enum_to_string(T e) noexcept {
return T~enum_values == e ...?
T~enum_names :
"unknown enum of type {}".format(T~string);
}

int main() {
// "alternatives" is an enum member of Foo.
static_assert(Foo::alternatives~is_enum);

// It has enumerators for each choice alternative.
std::cout<< "alternatives enumerators:\n";
std::cout<< " {} = {}\n".format(Foo::alternatives~enum_names,
Foo::alternatives~enum_values~to_underlying) ...;

// Naming a choice alternative gives you back an enumerator.
static_assert(Foo::alternatives == decltype(Foo::x));

// Foo::y is an enumerator of type Foo::alternatives. But it's also
// how you construct choice types! The enumerator has been overloaded to
// work as an initializer.

// Foo::y is type Foo::alternatives.
static_assert(Foo::alternatives == decltype(Foo::y));

// Foo::y() is an initializer, so Foo::y() is type Foo.
static_assert(Foo == decltype(Foo::y()));

// Initialize a Foo object.
Foo obj = .y(3.14);

// .active is an implicit data member set to the active alternative.
// The type is Foo::alternatives.
std::cout<< "obj.active = "<< enum_to_string(obj.active)<< "\n";

// Compare an enumerator with the .active member to see what's active.
if(Foo::x == obj.active)
std::cout<< "x is active\n";
else if(Foo::y == obj.active)
std::cout<< "y is active\n";
else if(Foo::z == obj.active)
std::cout<< "z is active\n";
}
39 changes: 39 additions & 0 deletions new-circle/choice7.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma feature choice
#include <type_traits>

struct A {
// Declare a non-trivial destructor to keep things interesting.
~A() { }

// Declare potentially-throwing copy constructor.
A(const A&) noexcept(false);

// We must define a non-throwing move constructor.
A(A&&) noexcept;

// Define a move assignment operator, because [class.copy.assign]/4
// prevents is generation.
A& operator=(A&&) noexcept;
};

choice my_choice_t {
// We need a choice type with at least two alternatives to get into
// a code path that calls copy constructors during choice assignment.
value(A),
value2(int),
};

// The choice type is *not* copy-assignable, because that could leave it in
// a valueles-by-exception state.
static_assert(!std::is_copy_assignable_v<my_choice_t>);

// However, it *is* move-assignable.
static_assert(std::is_move_assignable_v<my_choice_t>);

void copy_assign(my_choice_t& lhs, const my_choice_t& rhs) {
// Simulate copy-assignment in 2 steps:
// 1. Copy-construct the rhs.
// 2. Move-assign that temporary into the lhs.
// lhs = rhs; // ERROR!
lhs = my_choice_t(rhs); // OK!
}
Loading

0 comments on commit cd631a0

Please sign in to comment.