Skip to content

Commit

Permalink
Changes any / enum
Browse files Browse the repository at this point in the history
  • Loading branch information
lerno committed Jun 13, 2024
1 parent 1ff0922 commit 29acc89
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 61 deletions.
4 changes: 4 additions & 0 deletions src/content/docs/references/development/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ sidebar:
order: 205
---

##### Revision 2024-06-13
- Updated enum syntax
- `any` -> `any*`

##### Revision 2023-10-24
- Removal of `$checks` and `@checked`
- Additional chapter on vectors.
Expand Down
68 changes: 25 additions & 43 deletions src/content/docs/references/docs/anyinterfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ sidebar:
order: 122
---

## Working with the type of `any*` at runtime.
## Working with the type of `any` at runtime.

The `any*` type is recommended for writing code that is polymorphic at runtime where macros are not appropriate.
It can be thought of as a typed `void*`. Note that it is a fat pointer and is two pointers wide (unlike `void*`).
It cannot be dereferenced.
The `any` type is recommended for writing code that is polymorphic at runtime where macros are not appropriate.
It can be thought of as a typed `void*`.

An `any*` can be created by assigning any pointer to it. You can then query the `any*` type for the typeid of
An `any` can be created by assigning any pointer to it. You can then query the `any` type for the typeid of
the enclosed type (the type the pointer points to) using the `type` field.

This allows switching over the typeid, either using a normal switch:
Expand All @@ -24,7 +23,7 @@ This allows switching over the typeid, either using a normal switch:
...
}

Or the special `any*`-version of the switch:
Or the special `any`-version of the switch:

switch (my_any)
{
Expand All @@ -36,7 +35,7 @@ Or the special `any*`-version of the switch:

Sometimes one needs to manually construct an any-pointer, which
is typically done using the `any_make` function: `any_make(ptr, type)`
will create an `any*` pointing to `ptr` and with typeid `type`.
will create an `any` pointing to `ptr` and with typeid `type`.

Since the runtime `typeid` is available, we can query for any runtime `typeid` property available
at runtime, for example the size, e.g. `my_any.typeid.sizeof`. This allows us to do a lot of work
Expand Down Expand Up @@ -81,7 +80,7 @@ more challenging to evolve over time.
As an alternative there are languages (such as Objective-C) which instead use message passing to dynamically typed
objects, where the availability of a certain functionality may be queried at runtime.

C3 provides this latter functionality over the `any*` type using *interfaces*.
C3 provides this latter functionality over the `any` type using *interfaces*.

### Defining an interface

Expand All @@ -105,29 +104,12 @@ To declare that a type implements an interface, add it after the type name:
}

// Note how the first argument differs from the interface.
fn String Baz.myname(Baz* self) @dynamic
fn String Baz.myname(Baz self) @dynamic
{
return "I am Baz!";
}

If a type declares an interface but does not implement its methods, then that is compile time error. However,
methods marked `@optional` does not need to be implemented:

interface VeryOptional
{
fn void test();
fn void do_something(int x, void* ptr) @optional;
}

struct Foo (VeryOptional)
{
int z;
}

fn void Foo.test(&self) { }

This example is would compile, despite not implementing both functions, as the second method is marked `@optional`.

If a type declares an interface but does not implement its methods, then that is compile time error.
A type may implement multiple interfaces, by placing them all inside of `()` e.g. `struct Foo (VeryOptional, MyName) { ... }`

A limitation is that only user-defined types may declare they are implementing interfaces. To make existing types
Expand All @@ -136,9 +118,9 @@ implement interfaces is possible but does not provide compile time checks.
One of the interfaces available in the standard library is Printable, which contains `to_format` and `to_new_string`.
If we implemented it for our struct above it might look like this:

fn String Baz.to_new_string(Baz* baz, Allocator* using) @dynamic
fn String Baz.to_new_string(Baz baz, Allocator allocator) @dynamic
{
return string::printf("Baz(%d)", baz.x, .using = using);
return string::printf("Baz(%d)", baz.x, .allocator = allocator);
}

### "@dynamic" methods
Expand All @@ -157,33 +139,33 @@ from the `any` type.

### Referring to an interface by pointer

A pointer to an interface e.g. `MyName*` is can be cast back and forth to `any*`, but only types which
implement the interface completely may implicitly be cast to the interface pointer.
An interface e.g. `MyName` is can be cast back and forth to `any`, but only types which
implement the interface completely may implicitly be cast to the interface.

So for example:

Bob b = { 1 };
double d = 0.5;
int i = 3;
MyName* a = &b; // Valid, Bob implements MyName.
// MyName* c = &d; // Error, double does not implement MyName.
MyName* c = (MyName*)&d; // Would break at runtime as double doesn't implement MyName
// MyName* z = &i; // Error, implicit conversion because int doesn't explicitly implement it.
MyName* z = (MyName*)&i; // Explicit conversion works and is safe at runtime if int implements "myname"
MyName a = &b; // Valid, Bob implements MyName.
// MyName c = &d; // Error, double does not implement MyName.
MyName c = (MyName)&d; // Would break at runtime as double doesn't implement MyName
// MyName z = &i; // Error, implicit conversion because int doesn't explicitly implement it.
MyName* z = (MyName)&i; // Explicit conversion works and is safe at runtime if int implements "myname"

### Calling dynamic methods

Methods implementing interfaces are like normal methods, and if called directly, they are just normal function calls. The
difference is that they may be invoked through the interface:

fn void whoareyou(MyName* a)
fn void whoareyou(MyName a)
{
io::printn(a.myname());
}

If we have an optional method we should first check that it is implemented:

fn void do_something(VeryOptional* z)
fn void do_something(VeryOptional z)
{
if (&z.do_something)
{
Expand All @@ -194,9 +176,9 @@ If we have an optional method we should first check that it is implemented:
We first query if the method exists on the value. If it does we actually run it.

Here is another example, showing how the correct function will be called depending on type, checking
for methods on an `any*`:
for methods on an `any`:

fn void whoareyou2(any* a)
fn void whoareyou2(any a)
{
// Query if the function exists
if (!&a.myname)
Expand All @@ -205,7 +187,7 @@ for methods on an `any*`:
return;
}
// Dynamically call the function
io::printn(((MyName*)a).myname());
io::printn(((MyName)a).myname());
}

fn void main()
Expand All @@ -214,7 +196,7 @@ for methods on an `any*`:
double d;
Bob bob;

any* a = &i;
any a = &i;
whoareyou2(a); // Prints "I am int!"
a = &d;
whoareyou2(a); // Prints "I don't know who I am."
Expand All @@ -237,7 +219,7 @@ It is possible to retrieve any `@dynamic` function by name and invoke it:
fn void main()
{
int z = 321;
any* a = &z;
any a = &z;
VoidMethodFn test_func = a.reflect("test_something");
test_func(a); // Will print "Testing: 321"
}
Expand Down
6 changes: 3 additions & 3 deletions src/content/docs/references/docs/reflection.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ Returns the alignment in bytes needed for the type.
*Only available for enums.*
Returns an array containing the types of associated values if any.

enum Foo : int(double d, String s)
enum Foo : int (double d, String s)
{
BAR(1.0, "normal"),
BAZ(2.0, "exceptional")
BAR = { 1.0, "normal" },
BAZ = { 2.0, "exceptional" }
}
String s = Foo.associated[0].nameof; // "double"

Expand Down
10 changes: 5 additions & 5 deletions src/content/docs/references/docs/specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -545,9 +545,9 @@ alignment.

The typeid is a pointer sized value which uniquely identifies a type.

### Any* type
### Any type

The `any*` is a fat pointer (2 pointers wide) holding a pointer to a value and its corresponding [typeid](#typeid-type).
The `any` is a fat pointer (2 pointers wide) holding a pointer to a value and its corresponding [typeid](#typeid-type).
It cannot be dereferenced.

#### Fields
Expand Down Expand Up @@ -1251,8 +1251,8 @@ be evaluated in order after the cond expression has been evaluated (if it exists

#### Any-switch

If the cond expression is an `any*` type, the switch is handled as if switching was done over the `type`
field of the `any*`. This field has the type of [typeid](#typeid-type), and the cases follows the rules
If the cond expression is an `any` type, the switch is handled as if switching was done over the `type`
field of the `any`. This field has the type of [typeid](#typeid-type), and the cases follows the rules
for [switching over typeid](#switching-over-typeid).

If the cond expression is a variable, then this variable is implicitly converted to a pointer with
Expand All @@ -1261,7 +1261,7 @@ the pointee type given by the case statement.
Example:

```c
any* a = abc();
any a = abc();
switch (a)
{
case int:
Expand Down
6 changes: 3 additions & 3 deletions src/content/docs/references/docs/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,9 @@ It is possible to associate each enum value with a static value.

enum State : int (String state_desc, bool active)
{
PENDING("pending start", false),
RUNNING("running", true),
TERMINATED("ended", false)
PENDING = { "pending start", false },
RUNNING = { "running", true },
TERMINATED = { "ended", false }
}

...
Expand Down
14 changes: 7 additions & 7 deletions src/content/docs/references/getting-started/allfeatures.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Changes relating to literals, identifiers etc.
10. Optional types are formed using the `!` suffix.
11. `bool` is the boolean type.
12. `typeid` is a unique type identifier for a type, it can be used at runtime and compile time.
13. `any*` contains a `typeid` and `void*` allowing it to act as a reference to any type of value.
13. `any` contains a `typeid` and `void*` allowing it to act as a reference to any type of value.
14. `anyfault` holds any `fault` value (see below).

### Changed
Expand Down Expand Up @@ -81,7 +81,7 @@ Changes relating to literals, identifiers etc.
11. Using `inline` on a distinct type allows it to be implicitly converted *to* its base type (but not vice versa).
12. Types may add operator overloading to support `foreach` and subscript operations.
13. Generic types through generic modules, using `(< ... >)` for the generic parameter list (e.g. `List(<int>) list;`).
14. Interface types, `any*` types which allows dynamic invocation of methods.
14. Interface types, `any` types which allows dynamic invocation of methods.

### Changed

Expand Down Expand Up @@ -114,7 +114,7 @@ Runtime type methods: `inner`, `kind`, `len`, `names`, `sizeof`.
3. `?:` operator, returning the first value if it can be converted to a boolean true, otherwise the second value is returned.
4. Orelse `??` returning the first value if it is a result, the second if the first value was an optional value.
5. Rethrow `!` suffix operator with an implicit `return` the value if it was an optional value.
6. Dynamic calls, allowing calls to be made on the `any*` and interfaces dispatched using a dynamic mechanism.
6. Dynamic calls, allowing calls to be made on the `any` and interfaces dispatched using a dynamic mechanism.
7. Create a slice using a range subscript (e.g. `a[4..8]` to form a slice from element 4 to element 8).
8. Two range subscript methods: `[start..inclusive_end]` and `[start:length]`. Start, end and length may be omitted for default values.
9. Indexing from end: slices, arrays and vectors may be indexed from the end using `^`. `^1` represents the last element. This works for ranges as well.
Expand All @@ -129,7 +129,7 @@ Runtime type methods: `inner`, `kind`, `len`, `names`, `sizeof`.
18. Simple bitstructs (only containing booleans) may be manipulated using bit operations `& ^ | ~` and assignment.
19. Structs may implicitly convert to their `inline` member if they have one.
20. Pointers to arrays may implicitly convert to slices.
21. Any pointer may implicitly convert to an `any*` with type being the pointee.
21. Any pointer may implicitly convert to an `any` with type being the pointee.
22. Optional values will implicitly invoke "flatmap" on an expression it is a subexpression of.
23. Swizzling for arrays and vectors.

Expand All @@ -156,7 +156,7 @@ Runtime type methods: `inner`, `kind`, `len`, `names`, `sizeof`.
1. Functions may be invoked using named arguments, the name is the dot-prefixed parameter name, e.g. `foo(.name = a, .len = 2)`.
2. Typed varargs are declared `Type... argument`, and will take 0 or more arguments of the given type.
3. It is possible to "splat" an array or slice into the location of a typed vararg using `...`: `foo(a, b, ...list)`
4. `any*` varargs are declared `argument...`, it can take 0 or more arguments of any type which are implicitly converted to the `any*` type.
4. `any` varargs are declared `argument...`, it can take 0 or more arguments of any type which are implicitly converted to the `any` type.
5. The function declaration may have `@inline` or `@noinline` as a default.
6. Using `@inline` or `@noinline` on a function call expression will override the function default.
7. Type methods are functions defined in the form `fn void Foo.my_method(Foo* foo) { ... }`, they can be invoked using dot syntax.
Expand Down Expand Up @@ -212,7 +212,7 @@ The complete list: `@align`, `@benchmark`, `@bigendian`, `@builtin`,

1. Match-style variant of the `switch` statement, allows each `case` to hold an expression to test.
2. Switching over type with `typeid`.
3. Unpack `any*` to the underlying type with an `any`-switch.
3. Unpack `any` to the underlying type with an `any`-switch.
4. `nextcase` to fallthrough to the next case.
5. `nextcase <expr>` to jump to the case with the expression value (this may be an expression evaluated at runtime).
6. `nextcase default` to jump to the `default` clause.
Expand Down Expand Up @@ -305,7 +305,7 @@ Some features are provided by builtins, and appears as normal functions and macr
but nonetheless provided unique functionality:

1. `@likely(...)` / `@unlikely(...)` on branches affects compilation optimization.
2. `@anycast(...)` casts an `any*` with an optional result.
2. `@anycast(...)` casts an `any` with an optional result.
3. `unreachable(...)` marks a path as unreachable with a panic in safe mode.
4. `unsupported(...)` similar to unreachable but for functionality not implemented.
5. `@expect(...)` expect a certain value with an optional probability for the optimizer.
Expand Down

0 comments on commit 29acc89

Please sign in to comment.