package core.alloc.heap
-#local _Z :: (package core.intrinsics.onyx).__zero_value
-
AutoHeapState :: struct {
backing_allocator: Allocator;
- set := _Z(Set(rawptr));
+ set := Set(rawptr).{};
}
#local auto_heap_alloc_proc :: (data: ^AutoHeapState, aa: AllocationAction, size: u32, align: u32, oldptr: rawptr) -> rawptr {
package core.array
-use package core.intrinsics.onyx { __zero_value }
-
// [..] T == Array(T)
// where
// Array :: struct (T: type_expr) {
}
delete :: (arr: ^[..] $T, idx: u32) -> T {
- if idx >= arr.count do return __zero_value(T);
+ if idx >= arr.count do return .{};
to_return := arr.data[idx];
for i: idx .. arr.count - 1 {
}
fast_delete :: (arr: ^[..] $T, idx: u32) -> T {
- if idx >= arr.count do return __zero_value(T);
+ if idx >= arr.count do return .{};
to_return := arr.data[idx];
if idx != arr.count - 1 do arr.data[idx] = arr.data[arr.count - 1];
}
pop :: (arr: ^[..] $T) -> T {
- if arr.count == 0 do return __zero_value(T);
+ if arr.count == 0 do return .{};
arr.count -= 1;
return arr.data[arr.count];
}
get :: (arr: [] $T, idx: i32) -> T {
- if arr.count == 0 do return __zero_value(T);
+ if arr.count == 0 do return .{};
while idx < 0 do idx += arr.count;
while idx >= arr.count do idx -= arr.count;
bucket := ^ba.buckets[bucket_idx];
while elem_idx == bucket.count {
bucket_idx += 1;
- if bucket_idx == ba.buckets.count do return __zero_value(T), false;
+ if bucket_idx == ba.buckets.count do return .{}, false;
bucket = ^ba.buckets[bucket_idx];
elem_idx = 0;
package core.iter
-use package core.intrinsics.onyx { __zero_value }
#local memory :: package core.memory
#local alloc :: package core.alloc
next :: (mi: ^MapIterator($T, $R)) -> (R, bool) {
value, cont := mi.iterator.next(mi.iterator.data);
- if !cont do return __zero_value(R), false;
+ if !cont do return .{}, false;
return mi.transform(value), true;
}
next :: (mi: ^MapIterator($T, $R, $Ctx)) -> (R, bool) {
value, cont := mi.iterator.next(mi.iterator.data);
- if !cont do return __zero_value(R), false;
+ if !cont do return .{}, false;
return mi.transform(value, mi.ctx), true;
}
take_iterator.allocator = allocator;
next :: ($T: type_expr, ti: ^TakeIterator(T)) -> (T, bool) {
- if ti.remaining == 0 do return __zero_value(T), false;
+ if ti.remaining == 0 do return .{}, false;
ti.remaining -= 1;
return ti.iterator.next(ti.iterator.data);
next :: (use c: ^Context($T)) -> (T, bool) {
while true {
- if idx >= iters.count do return __zero_value(T), false;
+ if idx >= iters.count do return .{}, false;
curr_iter := ^iters[idx];
value, valid := curr_iter.next(curr_iter.data);
next :: (use data: ^Enumeration_Context($T)) -> (Enumeration_Value(T), bool) {
value, cont := iterator.next(iterator.data);
- if !cont do return .{ current_index, __zero_value(T) }, false;
+ if !cont do return .{ current_index, .{} }, false;
defer current_index += 1;
return .{ current_index, value }, true;
return arr.data[current], true;
} else {
- return __zero_value(T), false;
+ return .{}, false;
}
}
}
next :: (use c: ^Context($T)) -> (T, bool) {
- if ended do return __zero_value(T), false;
+ if ended do return .{}, false;
sync.scoped_mutex(^mutex);
if v, success := take_one(iterator); !success {
package core.list
-use package core.intrinsics.onyx { __zero_value }
-
ListElem :: struct (T: type_expr) {
next: ^ListElem(T) = null;
prev: ^ListElem(T) = null;
if list.last == null do list.last = new_elem;
}
-pop_end :: (list: ^List($T), default: T = __zero_value(T)) -> T {
+pop_end :: (list: ^List($T), default: T = .{}) -> T {
if list.last == null do return default;
end := list.last;
return end.data;
}
-pop_begin :: (list: ^List($T), default: T = __zero_value(T)) -> T {
+pop_begin :: (list: ^List($T), default: T = .{}) -> T {
if list.last == null do return default;
begin := list.first;
#match iter.as_iterator get_iterator
get_iterator :: (list: ^List($T)) -> Iterator(T) {
iterator_next :: (list_iter: ^ListIterator($T)) -> (T, bool) {
- use package core.intrinsics.onyx { __zero_value }
- if list_iter.current == null do return __zero_value(T), false;
+ if list_iter.current == null do return .{}, false;
defer list_iter.current = list_iter.current.next;
return list_iter.current.data, true;
math :: package core.math
conv :: package core.conv
- use package core.intrinsics.onyx { __zero_value, __initialize }
+ use package core.intrinsics.onyx { __initialize }
}
#local {
empty :: (package core.map).empty
}
-make :: ($Key: type_expr, $Value: type_expr, default := __zero_value(Value)) -> Map(Key, Value) {
+make :: ($Key: type_expr, $Value: type_expr, default := Value.{}) -> Map(Key, Value) {
map : Map(Key, Value);
init(^map, default = default);
return map;
#match (package builtin).__make_overload macro (x: ^Map($K, $V), allocator := context.allocator) => (package core.map).make(K, V);
-init :: (use map: ^Map($K, $V), default := __zero_value(V)) {
+init :: (use map: ^Map($K, $V), default := V.{}) {
__initialize(map);
allocator = context.allocator;
memory :: package core.memory
math :: package core.math
}
-use package core.intrinsics.onyx { __zero_value }
#local SetValue :: interface (t: $T) {
{ hash.to_u32(t) } -> u32;
iterator :: iterator
}
-make :: ($T: type_expr, default := __zero_value(T), allocator := context.allocator) -> Set(T) {
+make :: ($T: type_expr, default := T.{}, allocator := context.allocator) -> Set(T) {
set : Set(T);
init(^set, default=default, allocator=allocator);
return set;
#match (package builtin).__make_overload macro (x: ^Set, allocator: Allocator) => (package core.set).make(x.Elem_Type, allocator = allocator);
-init :: (set: ^Set($T), default := __zero_value(T), allocator := context.allocator) {
+init :: (set: ^Set($T), default := T.{}, allocator := context.allocator) {
set.allocator = allocator;
set.default_value = default;
get :: (use set: ^Set, value: set.Elem_Type) -> set.Elem_Type {
lr := lookup(set, value);
- return entries[lr.entry_index].value if lr.entry_index >= 0 else __zero_value(set.Elem_Type);
+ return entries[lr.entry_index].value if lr.entry_index >= 0 else set.Elem_Type.{};
}
get_ptr :: (use set: ^Set, value: set.Elem_Type) -> ^set.Elem_Type {
return set.entries[position].value, true;
} else {
- return __zero_value(T), false;
+ return .{}, false;
}
}
package core.intrinsics.onyx
__initialize :: (val: ^$T) -> void #intrinsic ---
-__zero_value :: ($T: type_expr) -> T #intrinsic ---
init :: macro ($T: type_expr) -> T {
__initialize :: __initialize
memory :: package core.memory
alloc :: package core.alloc
os :: package core.os
-
- use package core.intrinsics.onyx { __zero_value }
}
#if !runtime.Multi_Threading_Enabled {
tcp_get_events :: (use conn: ^TCP_Connection) -> Iterator(TCP_Event) {
next :: (use conn: ^TCP_Connection) -> (TCP_Event, bool) {
- if event_cursor == events.count do return __zero_value(TCP_Event), false;
+ if event_cursor == events.count do return .{}, false;
defer event_cursor += 1;
return events[event_cursor], true;
}
next :: (use c: ^Context) -> (DirectoryEntry, bool) {
- use package core.intrinsics.onyx {__zero_value}
- if !opened do return __zero_value(DirectoryEntry), false;
+ if !opened do return .{}, false;
entry: DirectoryEntry;
if !dir_read(dir, ^entry) {
- return __zero_value(DirectoryEntry), false;
+ return .{}, false;
}
return entry, true;
// Here, Vector2 has the type constraint of NumberLike for T. This constraint
// is checked when Vector2 is constructed with any parameters.
Vector2 :: struct (T: type_expr) where NumberLike(T) {
- x := __zero_value(T);
- y := __zero_value(T);
+ x := T.{};
+ y := T.{};
}
#operator + (x, y: Vector2($T)) => Vector2(T).{ x.x + y.x, x.y + y.y };
\
NODE(ForeignBlock) \
\
- NODE(Package)
+ NODE(Package) \
+ \
+ NODE(ZeroValue)
#define NODE(name) typedef struct Ast ## name Ast ## name;
AST_NODES
Ast_Kind_Foreign_Block,
+ Ast_Kind_Zero_Value,
+
Ast_Kind_Note,
Ast_Kind_Count
ONYX_INTRINSIC_MEMORY_SIZE, ONYX_INTRINSIC_MEMORY_GROW,
ONYX_INTRINSIC_MEMORY_COPY, ONYX_INTRINSIC_MEMORY_FILL,
- ONYX_INTRINSIC_INITIALIZE, ONYX_INTRINSIC_ZERO_VALUE,
+ ONYX_INTRINSIC_INITIALIZE,
ONYX_INTRINSIC_I32_CLZ, ONYX_INTRINSIC_I32_CTZ, ONYX_INTRINSIC_I32_POPCNT,
ONYX_INTRINSIC_I32_AND, ONYX_INTRINSIC_I32_OR, ONYX_INTRINSIC_I32_XOR,
AstBlock* block;
};
+struct AstZeroValue {
+ AstTyped_base;
+};
struct AstDirectiveSolidify {
AstTyped_base;
AstLocal* make_local(bh_allocator a, OnyxToken* token, AstType* type_node);
AstNode* make_symbol(bh_allocator a, OnyxToken* sym);
AstUnaryOp* make_cast(bh_allocator a, AstTyped* expr, Type* to);
+AstZeroValue* make_zero_value(bh_allocator a, OnyxToken *token, Type* type);
void arguments_initialize(Arguments* args);
-b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg);
+b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg, b32 insert_zero_values);
void arguments_ensure_length(Arguments* args, u32 count);
void arguments_copy(Arguments* dest, Arguments* src);
void arguments_clone(Arguments* dest, Arguments* src);
</dict>
</dict>
</dict>
+ <dict>
+ <key>match</key>
+ <string>\b([#]\s*foreign)</string>
+ <key>captures</key>
+ <dict>
+ <key>1</key>
+ <dict>
+ <key>name</key>
+ <string>keyword.tag.onyx entity.name.block.onyx</string>
+ </dict>
+ </dict>
+ </dict>
<dict>
<key>match</key>
<string>\b(\b[[:alpha:]_]+[[:alnum:]_\.]*\b)\s*[\(]</string>
<string>string.quoted.double.onyx</string>
<key>patterns</key>
<array>
- <dict>
- <key>include</key>
- <string>#string_placeholder</string>
- </dict>
<dict>
<key>include</key>
<string>#string_escaped_char</string>
},
"declarations": [
"entity.name.function.onyx",
- "entity.name.type.onyx"
+ "entity.name.type.onyx",
+ "entity.name.block.onyx"
],
"indentation": {
"punctuation.block.begin.onyx": 1,
package ui
use package core
-use package core.intrinsics.onyx { __zero_value, __initialize }
+use package core.intrinsics.onyx { __initialize }
Workspace_State :: struct {
transform: gfx.Transform = .{
package vecmath
#local io :: package core.io
-use package core.intrinsics.onyx { __zero_value }
Vector2i :: #type Vector2(i32);
Vector2f :: #type Vector2(f32);
Vector2 :: struct (T: type_expr) {
- x := __zero_value(T);
- y := __zero_value(T);
+ x := T.{};
+ y := T.{};
}
#operator + vector2_add
package wasm_utils
-Z :: (package core.intrinsics.onyx).__zero_value
-
WasmInstructionCode :: enum {
unreachable :: 0x00;
nop :: 0x01;
data.reader = io.reader_make(^data.stream);
next :: (use c: ^CodeContext) -> (WasmInstruction, bool) {
- if current_block_depth == 0 do return Z(WasmInstruction), false;
+ if current_block_depth == 0 do return .{}, false;
return parse_instruction(^reader, binary, code.code_offset, ^current_block_depth), true;
}
Version :: SemVer.{0, 1, 1}
use core
-use core.intrinsics.onyx {__initialize, __zero_value}
+use core.intrinsics.onyx {__initialize}
global_arguments: struct {
#tag "--config-file"
"DO BLOCK",
"FOREIGN BLOCK",
+ "ZERO VALUE",
"NOTE",
if (node->kind == Ast_Kind_Struct_Literal && (node->type_node == NULL && node->type == NULL)) {
if (node->entity != NULL) return TYPE_MATCH_SUCCESS;
if (type->kind == Type_Kind_VarArgs) type = type->VarArgs.elem;
- if (!type_is_sl_constructable(type)) return TYPE_MATCH_FAILED;
+
+ //
+ // If the structure literal has arguments, and the type is not constructable
+ // using a struct literal, then they cannot be unified. However, if no arguments
+ // are given, e.g. .{}, then any type should be matched, as that is the universal
+ // zero-value.
+ if (!type_is_sl_constructable(type)) {
+ AstStructLiteral *sl = (AstStructLiteral *) node;
+ if (bh_arr_length(sl->args.values) != 0 || bh_arr_length(sl->args.named_values) != 0) {
+ return TYPE_MATCH_FAILED;
+ }
+ }
// If this shouldn't make permanent changes and submit entities,
// just assume that it works and don't submit the entities.
}
}
+ else if (node->kind == Ast_Kind_Zero_Value) {
+ if (node_type == NULL) {
+ node->type = type;
+ return TYPE_MATCH_SUCCESS;
+ }
+ }
+
return TYPE_MATCH_FAILED;
}
return cast;
}
+AstZeroValue* make_zero_value(bh_allocator a, OnyxToken* token, Type* type) {
+ AstZeroValue* zero_value = onyx_ast_node_new(a, sizeof(AstZeroValue), Ast_Kind_Zero_Value);
+ zero_value->token = token;
+ zero_value->flags |= Ast_Flag_Comptime;
+ zero_value->type = type;
+ return zero_value;
+}
+
void arguments_initialize(Arguments* args) {
if (args->values == NULL) bh_arr_new(global_heap_allocator, args->values, 2);
if (args->named_values == NULL) bh_arr_new(global_heap_allocator, args->named_values, 2);
{ "memory_fill", ONYX_INTRINSIC_MEMORY_FILL },
{ "__initialize", ONYX_INTRINSIC_INITIALIZE },
- { "__zero_value", ONYX_INTRINSIC_ZERO_VALUE },
{ "clz_i32", ONYX_INTRINSIC_I32_CLZ },
{ "ctz_i32", ONYX_INTRINSIC_I32_CTZ },
arguments_ensure_length(&call->args, arg_count);
char* err_msg = NULL;
- fill_in_arguments(&call->args, (AstNode *) callee, &err_msg);
+ fill_in_arguments(&call->args, (AstNode *) callee, &err_msg, 0);
if (err_msg != NULL) ERROR(call->token->pos, err_msg);
bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
}
if (!type_is_structlike_strict(sl->type)) {
+ //
+ // If there are no given arguments to a structure literal, it is treated as a 'zero-value',
+ // and can be used to create a completely zeroed value of any type.
+ if (bh_arr_length(sl->args.values) == 0 && bh_arr_length(sl->args.named_values) == 0) {
+ AstZeroValue *zv = make_zero_value(context.ast_alloc, sl->token, sl->type);
+ bh_arr_push(sl->args.values, (AstTyped *) zv);
+
+ sl->flags |= Ast_Flag_Has_Been_Checked;
+ return Check_Success;
+ }
+
+ if ((sl->flags & Ast_Flag_Has_Been_Checked) != 0) {
+ assert(sl->args.values);
+ assert(sl->args.values[0]);
+ assert(sl->args.values[0]->kind == Ast_Kind_Zero_Value);
+ return Check_Success;
+ }
+
+ //
+ // Otherwise, it is not possible to construct the type if it is not a structure.
ERROR_(sl->token->pos,
"'%s' is not constructable using a struct literal.",
type_get_name(sl->type));
// :Idempotency
if ((sl->flags & Ast_Flag_Has_Been_Checked) == 0) {
char* err_msg = NULL;
- if (!fill_in_arguments(&sl->args, (AstNode *) sl, &err_msg)) {
+ if (!fill_in_arguments(&sl->args, (AstNode *) sl, &err_msg, 1)) {
onyx_report_error(sl->token->pos, Error_Critical, err_msg);
bh_arr_each(AstTyped *, value, sl->args.values) {
case Ast_Kind_Constraint_Sentinel: break;
case Ast_Kind_Switch_Case: break;
case Ast_Kind_Foreign_Block: break;
+ case Ast_Kind_Zero_Value: break;
default:
retval = Check_Error;
arguments_ensure_length(&args, get_argument_buffer_size(&overload->type->Function, &args));
// NOTE: If the arguments cannot be placed successfully in the parameters list
- if (!fill_in_arguments(&args, (AstNode *) overload, NULL)) continue;
+ if (!fill_in_arguments(&args, (AstNode *) overload, NULL, 0)) continue;
VarArgKind va_kind;
TypeMatch tm = check_arguments_against_type(&args, &overload->type->Function, &va_kind, NULL, NULL, NULL);
// NOTE: The values array can be partially filled out, and is the resulting array.
// Returns if all the values were filled in.
-b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg) {
+b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg, b32 insert_zero_values) {
{ // Delete baked arguments
// :ArgumentResolvingIsComplicated
fori (idx, 0, bh_arr_length(args->values)) {
if (args->values[idx] == NULL) args->values[idx] = (AstTyped *) lookup_default_value_by_idx(provider, idx);
if (args->values[idx] == NULL) {
- if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "No value given for %d%s argument.", idx + 1, bh_num_suffix(idx + 1));
- success = 0;
+ if (insert_zero_values) {
+ assert(provider->token);
+ args->values[idx] = (AstTyped *) make_zero_value(context.ast_alloc, provider->token, NULL);
+ } else {
+ if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "No value given for %d%s argument.", idx + 1, bh_num_suffix(idx + 1));
+ success = 0;
+ }
}
}
break;
}
- case ONYX_INTRINSIC_ZERO_VALUE: {
- // NOTE: This probably will not have to make an allocation.
- Type* zero_type = type_build_from_ast(context.ast_alloc, (AstType *) ((AstArgument *) call->original_args.values[0])->value);
- emit_zero_value_for_type(mod, &code, zero_type, call->token);
- break;
- }
-
case ONYX_INTRINSIC_I32_CLZ: WI(WI_I32_CLZ); break;
case ONYX_INTRINSIC_I32_CTZ: WI(WI_I32_CTZ); break;
case ONYX_INTRINSIC_I32_POPCNT: WI(WI_I32_POPCNT); break;
break;
}
+ case Ast_Kind_Zero_Value: {
+ AstZeroValue *zv = (AstZeroValue *) expr;
+ assert(zv->type);
+ emit_zero_value_for_type(mod, &code, zv->type, zv->token);
+ break;
+ }
+
default:
bh_printf("Unhandled case: %d\n", expr->kind);
DEBUG_HERE;
break;
}
+ case Ast_Kind_Zero_Value: {
+ memset(data, 0, type_size_of(node->type));
+ break;
+ }
+
case Ast_Kind_NumLit: {
// NOTE: This makes a big assumption that we are running on a
// little endian machine, since WebAssembly is little endian