aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTyler Erickson <me@tylererickson.com>2020-03-05 16:49:53 -0800
committerTyler Erickson <me@tylererickson.com>2020-03-05 16:49:53 -0800
commitbb026c99a93bc27ac89a8b1bf727b5b09799fe1c (patch)
tree07a3707a3d0b665cfb03c4066d0c4552dc5b0cec /src
parent2817bab494a093b861b33cba982d87d178e80501 (diff)
parent2fe0eaf2adf952867d4ce4fba53b4b3ac75e1ba5 (diff)
Merged with master
Diffstat (limited to 'src')
-rw-r--r--src/build_settings.cpp1
-rw-r--r--src/check_decl.cpp2
-rw-r--r--src/check_expr.cpp133
-rw-r--r--src/check_stmt.cpp14
-rw-r--r--src/check_type.cpp15
-rw-r--r--src/checker_builtin_procs.hpp4
-rw-r--r--src/entity.cpp1
-rw-r--r--src/ir.cpp79
-rw-r--r--src/ir_print.cpp44
-rw-r--r--src/main.cpp46
-rw-r--r--src/parser.cpp31
-rw-r--r--src/parser.hpp4
-rw-r--r--src/types.cpp51
13 files changed, 341 insertions, 84 deletions
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 5a1ec0f30..c264fbaaa 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -126,6 +126,7 @@ struct BuildContext {
bool use_lld;
bool vet;
bool cross_compiling;
+ bool use_subsystem_windows;
QueryDataSetSettings query_data_set_settings;
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 0e669e473..ece38e84f 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -1207,7 +1207,7 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
}
- bool where_clause_ok = evaluate_where_clauses(ctx, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+ bool where_clause_ok = evaluate_where_clauses(ctx, nullptr, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
if (!where_clause_ok) {
// NOTE(bill, 2019-08-31): Don't check the body as the where clauses failed
return;
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index b854a693b..1a01eef31 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -2260,6 +2260,8 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
x->mode = Addressing_Value;
} else if (is_type_slice(type) && is_type_string(x->type)) {
x->mode = Addressing_Value;
+ } else if (is_type_union(type)) {
+ x->mode = Addressing_Value;
}
return true;
}
@@ -5278,6 +5280,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
+ case BuiltinProc_cpu_relax:
+ operand->mode = Addressing_NoValue;
+ break;
+
case BuiltinProc_atomic_fence:
case BuiltinProc_atomic_fence_acq:
case BuiltinProc_atomic_fence_rel:
@@ -5942,7 +5948,9 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
Entity *e = sig_params[operand_index];
Type *t = e->type;
Operand o = operands[operand_index];
- call->viral_state_flags |= o.expr->viral_state_flags;
+ if (o.expr != nullptr) {
+ call->viral_state_flags |= o.expr->viral_state_flags;
+ }
if (e->kind == Entity_TypeName) {
// GB_ASSERT(!variadic);
@@ -5983,6 +5991,15 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
}
score += s;
+ if (e->flags & EntityFlag_ConstInput) {
+ if (o.mode != Addressing_Constant) {
+ if (show_error) {
+ error(o.expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string));
+ }
+ err = CallArgumentError_NoneConstantParameter;
+ }
+ }
+
if (o.mode == Addressing_Type && is_type_typeid(e->type)) {
add_type_info_type(c, o.type);
add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type));
@@ -6238,6 +6255,15 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
}
err = CallArgumentError_WrongTypes;
}
+
+ if (e->flags & EntityFlag_ConstInput) {
+ if (o->mode != Addressing_Constant) {
+ if (show_error) {
+ error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string));
+ }
+ err = CallArgumentError_NoneConstantParameter;
+ }
+ }
}
score += s;
}
@@ -6296,7 +6322,7 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize
}
-bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array<Ast *> *clauses, bool print_err) {
+bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, Array<Ast *> *clauses, bool print_err) {
if (clauses != nullptr) {
for_array(i, *clauses) {
Ast *clause = (*clauses)[i];
@@ -6304,9 +6330,11 @@ bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array<Ast *> *cla
check_expr(ctx, &o, clause);
if (o.mode != Addressing_Constant) {
if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation");
+ if (print_err && call_expr) error(call_expr, "at caller location");
return false;
} else if (o.value.kind != ExactValue_Bool) {
if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation");
+ if (print_err && call_expr) error(call_expr, "at caller location");
return false;
} else if (!o.value.value_bool) {
if (print_err) {
@@ -6348,6 +6376,7 @@ bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array<Ast *> *cla
}
}
+ if (call_expr) error(call_expr, "at caller location");
}
return false;
}
@@ -6613,7 +6642,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
ctx.curr_proc_sig = e->type;
GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- if (!evaluate_where_clauses(&ctx, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) {
+ if (!evaluate_where_clauses(&ctx, operand->expr, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) {
continue;
}
}
@@ -8572,8 +8601,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
o->expr = node;
return kind;
}
- Type *t = check_type(c, ta->type);
-
if (o->mode == Addressing_Constant) {
gbString expr_str = expr_to_string(o->expr);
error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str);
@@ -8594,54 +8621,80 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
bool src_is_ptr = is_type_pointer(o->type);
Type *src = type_deref(o->type);
- Type *dst = t;
Type *bsrc = base_type(src);
- Type *bdst = base_type(dst);
- if (is_type_union(src)) {
- bool ok = false;
- for_array(i, bsrc->Union.variants) {
- Type *vt = bsrc->Union.variants[i];
- if (are_types_identical(vt, dst)) {
- ok = true;
- break;
- }
+ if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) {
+ if (!is_type_union(src)) {
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %s", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
}
-
- if (!ok) {
- gbString expr_str = expr_to_string(o->expr);
- gbString dst_type_str = type_to_string(t);
- defer (gb_string_free(expr_str));
- defer (gb_string_free(dst_type_str));
- if (bsrc->Union.variants.count == 0) {
- error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
- } else {
- error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
- }
+ if (bsrc->Union.variants.count != 1) {
+ error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
add_type_info_type(c, o->type);
- add_type_info_type(c, t);
+ add_type_info_type(c, bsrc->Union.variants[0]);
- o->type = t;
+ o->type = bsrc->Union.variants[0];
o->mode = Addressing_OptionalOk;
- } else if (is_type_any(src)) {
- o->type = t;
- o->mode = Addressing_OptionalOk;
-
- add_type_info_type(c, o->type);
- add_type_info_type(c, t);
} else {
- gbString str = type_to_string(o->type);
- error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
+ Type *t = check_type(c, ta->type);
+ Type *dst = t;
+ Type *bdst = base_type(dst);
+
+
+ if (is_type_union(src)) {
+ bool ok = false;
+ for_array(i, bsrc->Union.variants) {
+ Type *vt = bsrc->Union.variants[i];
+ if (are_types_identical(vt, dst)) {
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ gbString expr_str = expr_to_string(o->expr);
+ gbString dst_type_str = type_to_string(t);
+ defer (gb_string_free(expr_str));
+ defer (gb_string_free(dst_type_str));
+ if (bsrc->Union.variants.count == 0) {
+ error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
+ } else {
+ error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
+ }
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, t);
+
+ o->type = t;
+ o->mode = Addressing_OptionalOk;
+ } else if (is_type_any(src)) {
+ o->type = t;
+ o->mode = Addressing_OptionalOk;
+
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, t);
+ } else {
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
}
add_package_dependency(c, "runtime", "type_assertion_check");
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index c365dca84..4b250c6a6 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -314,7 +314,11 @@ Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs)
gbString str = expr_to_string(lhs->expr);
if (e != nullptr && e->flags & EntityFlag_Param) {
- error(lhs->expr, "Cannot assign to '%s' which is a procedure parameter", str);
+ if (e->flags & EntityFlag_Using) {
+ error(lhs->expr, "Cannot assign to '%s' which is from a 'using' procedure parameter", str);
+ } else {
+ error(lhs->expr, "Cannot assign to '%s' which is a procedure parameter", str);
+ }
} else {
error(lhs->expr, "Cannot assign to '%s'", str);
}
@@ -497,6 +501,8 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
Entity *f = found->elements.entries[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, expr);
+ if (e->flags & EntityFlag_Value) uvar->flags |= EntityFlag_Value;
+ if (e->flags & EntityFlag_Param) uvar->flags |= EntityFlag_Param;
Entity *prev = scope_insert(ctx->scope, uvar);
if (prev != nullptr) {
gbString expr_str = expr_to_string(expr);
@@ -1102,6 +1108,12 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (type_expr != nullptr) { // Otherwise it's a default expression
Operand y = {};
check_expr_or_type(ctx, &y, type_expr);
+ if (y.mode != Addressing_Type) {
+ gbString str = expr_to_string(type_expr);
+ error(type_expr, "Expected a type as a case, got %s", str);
+ gb_string_free(str);
+ continue;
+ }
if (switch_kind == TypeSwitch_Union) {
GB_ASSERT(is_type_union(bt));
diff --git a/src/check_type.cpp b/src/check_type.cpp
index f21c1563b..6194951c9 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -527,7 +527,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
if (st->where_clauses.count > 0 && st->polymorphic_params == nullptr) {
error(st->where_clauses[0], "'where' clauses can only be used on structures with polymorphic parameters");
} else {
- bool where_clause_ok = evaluate_where_clauses(ctx, ctx->scope, &st->where_clauses, true);
+ bool where_clause_ok = evaluate_where_clauses(ctx, node, ctx->scope, &st->where_clauses, true);
}
check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context);
}
@@ -714,7 +714,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
if (ut->where_clauses.count > 0 && ut->polymorphic_params == nullptr) {
error(ut->where_clauses[0], "'where' clauses can only be used on unions with polymorphic parameters");
} else {
- bool where_clause_ok = evaluate_where_clauses(ctx, ctx->scope, &ut->where_clauses, true);
+ bool where_clause_ok = evaluate_where_clauses(ctx, node, ctx->scope, &ut->where_clauses, true);
}
@@ -748,11 +748,17 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
union_type->Union.variants = variants;
union_type->Union.no_nil = ut->no_nil;
+ union_type->Union.maybe = ut->maybe;
if (union_type->Union.no_nil) {
if (variants.count < 2) {
error(ut->align, "A union with #no_nil must have at least 2 variants");
}
}
+ if (union_type->Union.maybe) {
+ if (variants.count != 1) {
+ error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count);
+ }
+ }
if (ut->align != nullptr) {
i64 custom_align = 1;
@@ -1716,8 +1722,11 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
if (p->flags&FieldFlag_auto_cast) {
param->flags |= EntityFlag_AutoCast;
}
- param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
+ if (p->flags&FieldFlag_const) {
+ param->flags |= EntityFlag_ConstInput;
+ }
+ param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
add_entity(ctx->checker, scope, name, param);
if (is_using) {
add_entity_use(ctx, name, param);
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index 4981fdedb..7ef1be8b8 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -36,6 +36,8 @@ enum BuiltinProcId {
BuiltinProc_simd_vector,
BuiltinProc_soa_struct,
+ BuiltinProc_cpu_relax,
+
BuiltinProc_atomic_fence,
BuiltinProc_atomic_fence_acq,
BuiltinProc_atomic_fence_rel,
@@ -214,6 +216,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
{STR_LIT("soa_struct"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
+ {STR_LIT("cpu_relax"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+
{STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
diff --git a/src/entity.cpp b/src/entity.cpp
index 8273af3f1..b89522b07 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -46,6 +46,7 @@ enum EntityFlag {
EntityFlag_BitFieldValue = 1<<12,
EntityFlag_PolyConst = 1<<13,
EntityFlag_NotExported = 1<<14,
+ EntityFlag_ConstInput = 1<<15,
EntityFlag_Static = 1<<16,
diff --git a/src/ir.cpp b/src/ir.cpp
index 5d075d7dd..6a56eb387 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -197,6 +197,7 @@ gbAllocator ir_allocator(void) {
IR_INSTR_KIND(ZeroInit, struct { irValue *address; }) \
IR_INSTR_KIND(Store, struct { irValue *address, *value; bool is_volatile; }) \
IR_INSTR_KIND(Load, struct { Type *type; irValue *address; i64 custom_align; }) \
+ IR_INSTR_KIND(InlineCode, struct { BuiltinProcId id; Array<irValue *> operands; }) \
IR_INSTR_KIND(AtomicFence, struct { BuiltinProcId id; }) \
IR_INSTR_KIND(AtomicStore, struct { \
irValue *address, *value; \
@@ -1063,6 +1064,14 @@ irValue *ir_instr_load(irProcedure *p, irValue *address) {
return v;
}
+irValue *ir_instr_inline_code(irProcedure *p, BuiltinProcId id, Array<irValue *> operands) {
+ irValue *v = ir_alloc_instr(p, irInstr_InlineCode);
+ irInstr *i = &v->Instr;
+ i->InlineCode.id = id;
+ i->InlineCode.operands = operands;
+ return v;
+}
+
irValue *ir_instr_atomic_fence(irProcedure *p, BuiltinProcId id) {
irValue *v = ir_alloc_instr(p, irInstr_AtomicFence);
irInstr *i = &v->Instr;
@@ -1222,7 +1231,11 @@ irValue *ir_instr_union_tag_ptr(irProcedure *p, irValue *address) {
// i->UnionTagPtr.type = alloc_type_pointer(t_type_info_ptr);
Type *u = type_deref(ir_type(address));
+ if (is_type_union_maybe_pointer(u)) {
+ GB_PANIC("union #maybe UnionTagPtr");
+ }
i->UnionTagPtr.type = alloc_type_pointer(union_tag_type(u));
+
return v;
}
@@ -1234,6 +1247,9 @@ irValue *ir_instr_union_tag_value(irProcedure *p, irValue *address) {
if (address) address->uses += 1;
Type *u = type_deref(ir_type(address));
+ if (is_type_union_maybe_pointer(u)) {
+ GB_PANIC("union #maybe UnionTagValue");
+ }
i->UnionTagPtr.type = union_tag_type(u);
return v;
}
@@ -4351,7 +4367,9 @@ irValue *ir_emit_union_tag_value(irProcedure *proc, irValue *u) {
irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue *x) {
Type *t = ir_type(x);
- if (is_type_cstring(t)) {
+ if (is_type_pointer(t)) {
+ return ir_emit_comp(proc, op_kind, x, v_raw_nil);
+ } else if (is_type_cstring(t)) {
irValue *ptr = ir_emit_conv(proc, x, t_u8_ptr);
return ir_emit_comp(proc, op_kind, ptr, v_raw_nil);
} else if (is_type_any(t)) {
@@ -4396,6 +4414,12 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
} else if (is_type_union(t)) {
if (type_size_of(t) == 0) {
return ir_emit_comp(proc, op_kind, v_zero, v_zero);
+ } else if (is_type_union_maybe_pointer(t)) {
+ Type *bt = base_type(t);
+ irValue *ptr = ir_address_from_load_or_generate_local(proc, x);
+ ptr = ir_emit_bitcast(proc, ptr, alloc_type_pointer(bt->Union.variants[0]));
+ irValue *data = ir_emit_load(proc, ptr);
+ return ir_emit_comp_against_nil(proc, op_kind, data);
} else {
irValue *tag = ir_emit_union_tag_value(proc, x);
return ir_emit_comp(proc, op_kind, tag, v_zero);
@@ -5232,8 +5256,12 @@ void ir_emit_store_union_variant(irProcedure *proc, irValue *parent, irValue *va
Type *t = type_deref(ir_type(parent));
- irValue *tag_ptr = ir_emit_union_tag_ptr(proc, parent);
- ir_emit_store(proc, tag_ptr, ir_const_union_tag(t, variant_type));
+ if (is_type_union_maybe_pointer(t)) {
+ // No tag needed!
+ } else {
+ irValue *tag_ptr = ir_emit_union_tag_ptr(proc, parent);
+ ir_emit_store(proc, tag_ptr, ir_const_union_tag(t, variant_type));
+ }
}
irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
@@ -5701,7 +5729,7 @@ irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t) {
}
// TODO(bill): Actually figure out what the conversion needs to be correctly 'cause LLVM
- return ir_emit_bitcast(proc, value, dst);
+ return ir_emit_bitcast(proc, value, t);
}
@@ -5728,22 +5756,41 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token
GB_ASSERT_MSG(is_type_union(src), "%s", type_to_string(src_type));
Type *dst = tuple->Tuple.variables[0]->type;
-
irValue *value_ = ir_address_from_load_or_generate_local(proc, value);
- irValue *tag = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value_));
- irValue *dst_tag = ir_const_union_tag(src, dst);
+ irValue *tag = nullptr;
+ irValue *dst_tag = nullptr;
+ irValue *cond = nullptr;
+ irValue *data = nullptr;
+
+ irValue *gep0 = ir_emit_struct_ep(proc, v, 0);
+ irValue *gep1 = ir_emit_struct_ep(proc, v, 1);
+
+ if (is_type_union_maybe_pointer(src)) {
+ data = ir_emit_load(proc, ir_emit_conv(proc, value_, ir_type(gep0)));
+ } else {
+ tag = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value_));
+ dst_tag = ir_const_union_tag(src, dst);
+ }
irBlock *ok_block = ir_new_block(proc, nullptr, "union_cast.ok");
irBlock *end_block = ir_new_block(proc, nullptr, "union_cast.end");
- irValue *cond = ir_emit_comp(proc, Token_CmpEq, tag, dst_tag);
+
+ if (data != nullptr) {
+ GB_ASSERT(is_type_union_maybe_pointer(src));
+ cond = ir_emit_comp_against_nil(proc, Token_NotEq, data);
+ } else {
+ cond = ir_emit_comp(proc, Token_CmpEq, tag, dst_tag);
+ }
+
ir_emit_if(proc, cond, ok_block, end_block);
ir_start_block(proc, ok_block);
- irValue *gep0 = ir_emit_struct_ep(proc, v, 0);
- irValue *gep1 = ir_emit_struct_ep(proc, v, 1);
- irValue *data = ir_emit_load(proc, ir_emit_conv(proc, value_, ir_type(gep0)));
+
+ if (data == nullptr) {
+ data = ir_emit_load(proc, ir_emit_conv(proc, value_, ir_type(gep0)));
+ }
ir_emit_store(proc, gep0, data);
ir_emit_store(proc, gep1, v_true);
@@ -6876,6 +6923,9 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
// "Intrinsics"
+ case BuiltinProc_cpu_relax:
+ return ir_emit(proc, ir_instr_inline_code(proc, id, {}));
+
case BuiltinProc_atomic_fence:
case BuiltinProc_atomic_fence_acq:
case BuiltinProc_atomic_fence_rel:
@@ -7344,10 +7394,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
GB_ASSERT(are_types_identical(ir_type(left), key_type));
Type *it = bit_set_to_int(rt);
+ left = ir_emit_conv(proc, left, it);
irValue *lower = ir_value_constant(it, exact_value_i64(rt->BitSet.lower));
- irValue *key = ir_emit_arith(proc, Token_Sub, left, lower, ir_type(left));
- irValue *bit = ir_emit_arith(proc, Token_Shl, v_one, key, ir_type(left));
+ irValue *key = ir_emit_arith(proc, Token_Sub, left, lower, it);
+ irValue *bit = ir_emit_arith(proc, Token_Shl, v_one, key, it);
bit = ir_emit_conv(proc, bit, it);
irValue *old_value = ir_emit_bitcast(proc, right, it);
@@ -11465,6 +11516,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
irValue *tag_type_ptr = ir_emit_struct_ep(proc, tag, 2);
irValue *custom_align_ptr = ir_emit_struct_ep(proc, tag, 3);
irValue *no_nil_ptr = ir_emit_struct_ep(proc, tag, 4);
+ irValue *maybe_ptr = ir_emit_struct_ep(proc, tag, 5);
isize variant_count = gb_max(0, t->Union.variants.count);
irValue *memory_types = ir_type_info_member_types_offset(proc, variant_count);
@@ -11494,6 +11546,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
ir_emit_store(proc, custom_align_ptr, is_custom_align);
ir_emit_store(proc, no_nil_ptr, ir_const_bool(t->Union.no_nil));
+ ir_emit_store(proc, maybe_ptr, ir_const_bool(t->Union.maybe));
}
break;
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index 2baf1c2a0..bcbdca615 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -508,13 +508,25 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
// LLVM takes the first element's alignment as the entire alignment (like C)
i64 align = type_align_of(t);
+
+ if (is_type_union_maybe_pointer_original_alignment(t)) {
+ ir_write_byte(f, '{');
+ ir_print_type(f, m, t->Union.variants[0]);
+ ir_write_byte(f, '}');
+ return;
+ }
+
i64 block_size = t->Union.variant_block_size;
ir_write_byte(f, '{');
ir_print_alignment_prefix_hack(f, align);
- ir_fprintf(f, ", [%lld x i8], ", block_size);
- // ir_print_type(f, m, t_type_info_ptr);
- ir_print_type(f, m, union_tag_type(t));
+ if (is_type_union_maybe_pointer(t)) {
+ ir_fprintf(f, ", ");
+ ir_print_type(f, m, t->Union.variants[0]);
+ } else {
+ ir_fprintf(f, ", [%lld x i8], ", block_size);
+ ir_print_type(f, m, union_tag_type(t));
+ }
ir_write_byte(f, '}');
}
return;
@@ -1471,6 +1483,18 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
break;
}
+ case irInstr_InlineCode:
+ {
+ switch (instr->InlineCode.id) {
+ case BuiltinProc_cpu_relax:
+ ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");
+ break;
+ default: GB_PANIC("Unknown inline code %d", instr->InlineCode.id); break;
+ }
+ }
+ break;
+
+
case irInstr_AtomicFence:
ir_write_str_lit(f, "fence ");
switch (instr->AtomicFence.id) {
@@ -1850,6 +1874,12 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_UnionTagPtr: {
Type *et = ir_type(instr->UnionTagPtr.address);
+
+ Type *ut = type_deref(et);
+ if (is_type_union_maybe_pointer(ut)) {
+ GB_PANIC("union #maybe UnionTagPtr");
+ }
+
ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
Type *t = base_type(type_deref(et));
GB_ASSERT(is_type_union(t));
@@ -1869,10 +1899,16 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_UnionTagValue: {
Type *et = ir_type(instr->UnionTagValue.address);
- ir_fprintf(f, "%%%d = extractvalue ", value->index);
Type *t = base_type(et);
+
+ if (is_type_union_maybe_pointer(t)) {
+ GB_PANIC("union #maybe UnionTagValue");
+ }
+
+ ir_fprintf(f, "%%%d = extractvalue ", value->index);
GB_ASSERT(is_type_union(t));
+
ir_print_type(f, m, et);
ir_write_byte(f, ' ');
ir_print_value(f, m, instr->UnionTagValue.address, et);
diff --git a/src/main.cpp b/src/main.cpp
index 87669f8e3..077603b30 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -243,6 +243,7 @@ enum BuildFlagKind {
#if defined(GB_SYSTEM_WINDOWS)
BuildFlag_ResourceFile,
BuildFlag_WindowsPdbName,
+ BuildFlag_Subsystem,
#endif
BuildFlag_COUNT,
@@ -331,8 +332,9 @@ bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None);
#if defined(GB_SYSTEM_WINDOWS)
- add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String);
- add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String);
+ add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String);
+ add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String);
+ add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String);
#endif
GB_ASSERT(args.count >= 3);
@@ -764,7 +766,19 @@ bool parse_build_flags(Array<String> args) {
bad_flags = true;
}
break;
+ }
+ case BuildFlag_Subsystem: {
+ GB_ASSERT(value.kind == ExactValue_String);
+ String subsystem = value.value_string;
+ if (str_eq_ignore_case(subsystem, str_lit("console"))) {
+ build_context.use_subsystem_windows = false;
+ } else if (str_eq_ignore_case(subsystem, str_lit("windows"))) {
+ build_context.use_subsystem_windows = true;
+ } else {
+ gb_printf_err("Invalid -subsystem string, got %.*s, expected either 'console' or 'windows'\n", LIT(subsystem));
+ bad_flags = true;
+ }
break;
}
#endif
@@ -1180,6 +1194,10 @@ int main(int arg_count, char const **arg_ptr) {
return 1;
}
+ if (init_filename == "-help") {
+ build_context.show_help = true;
+ }
+
build_context.command = command;
if (!parse_build_flags(args)) {
@@ -1372,8 +1390,8 @@ int main(int arg_count, char const **arg_ptr) {
}
+ char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
if (!build_context.use_lld) { // msvc
-
if (build_context.has_resource) {
exit_code = system_exec_command_line_app("msvc-link",
"\"%.*src.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
@@ -1388,36 +1406,42 @@ int main(int arg_count, char const **arg_ptr) {
exit_code = system_exec_command_line_app("msvc-link",
"\"%.*slink.exe\" \"%.*s.obj\" \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
+ "/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %s "
"",
LIT(find_result.vs_exe_path), LIT(output_base), LIT(output_base), LIT(output_base), output_ext,
- link_settings, LIT(build_context.link_flags),
+ link_settings,
+ subsystem_str,
+ LIT(build_context.link_flags),
lib_str
);
} else {
exit_code = system_exec_command_line_app("msvc-link",
"\"%.*slink.exe\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
+ "/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %s "
"",
LIT(find_result.vs_exe_path), LIT(output_base), LIT(output_base), output_ext,
- link_settings, LIT(build_context.link_flags),
+ link_settings,
+ subsystem_str,
+ LIT(build_context.link_flags),
lib_str
);
}
} else { // lld
exit_code = system_exec_command_line_app("msvc-link",
"\"%.*s\\bin\\lld-link\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
+ "/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %s "
"",
LIT(build_context.ODIN_ROOT),
LIT(output_base), LIT(output_base), output_ext,
- link_settings, LIT(build_context.link_flags),
+ link_settings,
+ subsystem_str,
+ LIT(build_context.link_flags),
lib_str
);
}
@@ -1500,11 +1524,11 @@ int main(int arg_count, char const **arg_ptr) {
// Shared libraries are .dylib on MacOS and .so on Linux.
#if defined(GB_SYSTEM_OSX)
output_ext = STR_LIT(".dylib");
+ link_settings = "-dylib -dynamic";
#else
output_ext = STR_LIT(".so");
+ link_settings = "-shared";
#endif
-
- link_settings = "-shared";
} else {
// TODO: Do I need anything here?
link_settings = "";
diff --git a/src/parser.cpp b/src/parser.cpp
index ecb02c803..f89b5676b 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -929,7 +929,7 @@ Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_c
}
-Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil,
+Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
Token where_token, Array<Ast *> const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_UnionType);
result->UnionType.token = token;
@@ -937,6 +937,7 @@ Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymor
result->UnionType.polymorphic_params = polymorphic_params;
result->UnionType.align = align;
result->UnionType.no_nil = no_nil;
+ result->UnionType.maybe = maybe;
result->UnionType.where_token = where_token;
result->UnionType.where_clauses = where_clauses;
return result;
@@ -2091,6 +2092,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Ast *polymorphic_params = nullptr;
Ast *align = nullptr;
bool no_nil = false;
+ bool maybe = false;
CommentGroup *docs = f->lead_comment;
Token start_token = f->curr_token;
@@ -2118,10 +2120,19 @@ Ast *parse_operand(AstFile *f, bool lhs) {
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
}
no_nil = true;
- } else {
+ } else if (tag.string == "maybe") {
+ if (maybe) {
+ syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
+ }
+ maybe = true;
+ }else {
syntax_error(tag, "Invalid union tag '#%.*s'", LIT(tag.string));
}
}
+ if (no_nil && maybe) {
+ syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together");
+ }
+
Token where_token = {};
Array<Ast *> where_clauses = {};
@@ -2150,7 +2161,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Token close = expect_token(f, Token_CloseBrace);
- return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, where_token, where_clauses);
+ return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses);
} break;
case Token_enum: {
@@ -2350,6 +2361,12 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
operand = ast_type_assertion(f, operand, token, type);
} break;
+ case Token_Question: {
+ Token question = expect_token(f, Token_Question);
+ Ast *type = ast_unary_expr(f, question, nullptr);
+ operand = ast_type_assertion(f, operand, token, type);
+ } break;
+
default:
syntax_error(f->curr_token, "Expected a selector");
advance_token(f);
@@ -2981,6 +2998,7 @@ enum FieldPrefixKind {
FieldPrefix_Invalid = 0,
FieldPrefix_using,
+ FieldPrefix_const,
FieldPrefix_no_alias,
FieldPrefix_c_var_arg,
FieldPrefix_auto_cast,
@@ -3007,6 +3025,9 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
return FieldPrefix_c_var_arg;
}
break;
+
+ case Token_const:
+ return FieldPrefix_const;
}
return FieldPrefix_Unknown;
}
@@ -3019,6 +3040,7 @@ u32 parse_field_prefixes(AstFile *f) {
i32 no_alias_count = 0;
i32 c_vararg_count = 0;
i32 auto_cast_count = 0;
+ i32 const_count = 0;
for (;;) {
FieldPrefixKind kind = is_token_field_prefix(f);
@@ -3036,12 +3058,14 @@ u32 parse_field_prefixes(AstFile *f) {
case FieldPrefix_no_alias: no_alias_count += 1; advance_token(f); break;
case FieldPrefix_c_var_arg: c_vararg_count += 1; advance_token(f); break;
case FieldPrefix_auto_cast: auto_cast_count += 1; advance_token(f); break;
+ case FieldPrefix_const: const_count += 1; advance_token(f); break;
}
}
if (using_count > 1) syntax_error(f->curr_token, "Multiple 'using' in this field list");
if (no_alias_count > 1) syntax_error(f->curr_token, "Multiple '#no_alias' in this field list");
if (c_vararg_count > 1) syntax_error(f->curr_token, "Multiple '#c_vararg' in this field list");
if (auto_cast_count > 1) syntax_error(f->curr_token, "Multiple 'auto_cast' in this field list");
+ if (const_count > 1) syntax_error(f->curr_token, "Multiple '#const' in this field list");
u32 field_flags = 0;
@@ -3049,6 +3073,7 @@ u32 parse_field_prefixes(AstFile *f) {
if (no_alias_count > 0) field_flags |= FieldFlag_no_alias;
if (c_vararg_count > 0) field_flags |= FieldFlag_c_vararg;
if (auto_cast_count > 0) field_flags |= FieldFlag_auto_cast;
+ if (const_count > 0) field_flags |= FieldFlag_const;
return field_flags;
}
diff --git a/src/parser.hpp b/src/parser.hpp
index 983db1042..6426cc96b 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -203,12 +203,13 @@ enum FieldFlag {
FieldFlag_no_alias = 1<<2,
FieldFlag_c_vararg = 1<<3,
FieldFlag_auto_cast = 1<<4,
+ FieldFlag_const = 1<<5,
FieldFlag_Tags = 1<<10,
FieldFlag_Results = 1<<16,
- FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast,
+ FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const,
FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags,
};
@@ -515,6 +516,7 @@ AST_KIND(_TypeBegin, "", bool) \
Array<Ast *> variants; \
Ast *polymorphic_params; \
Ast * align; \
+ bool maybe; \
bool no_nil; \
Token where_token; \
Array<Ast *> where_clauses; \
diff --git a/src/types.cpp b/src/types.cpp
index e120c77c0..d2a040b0b 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -152,6 +152,7 @@ struct TypeUnion {
Type * polymorphic_params; // Type_Tuple
Type * polymorphic_parent;
bool no_nil;
+ bool maybe;
bool is_polymorphic;
bool is_poly_specialized;
};
@@ -1219,6 +1220,32 @@ bool is_type_map(Type *t) {
return t->kind == Type_Map;
}
+bool is_type_union_maybe_pointer(Type *t) {
+ t = base_type(t);
+ if (t->kind == Type_Union && t->Union.maybe) {
+ if (t->Union.variants.count == 1) {
+ return is_type_pointer(t->Union.variants[0]);
+ }
+ }
+ return false;
+}
+
+
+bool is_type_union_maybe_pointer_original_alignment(Type *t) {
+ t = base_type(t);
+ if (t->kind == Type_Union && t->Union.maybe) {
+ if (t->Union.variants.count == 1) {
+ Type *v = t->Union.variants[0];
+ if (is_type_pointer(v)) {
+ return type_align_of(v) == type_align_of(t);
+ }
+ }
+ }
+ return false;
+}
+
+
+
bool is_type_integer_endian_big(Type *t) {
t = core_type(t);
@@ -2024,6 +2051,7 @@ i64 union_tag_size(Type *u) {
Type *union_tag_type(Type *u) {
i64 s = union_tag_size(u);
switch (s) {
+ case 0: return t_u8;
case 1: return t_u8;
case 2: return t_u16;
case 4: return t_u32;
@@ -2934,14 +2962,23 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
}
}
- // NOTE(bill): Align to tag
- i64 tag_size = union_tag_size(t);
- i64 size = align_formula(max, tag_size);
- // NOTE(bill): Calculate the padding between the common fields and the tag
- t->Union.tag_size = tag_size;
- t->Union.variant_block_size = size - field_size;
+ i64 size = 0;
+
+ if (is_type_union_maybe_pointer(t)) {
+ size = max;
+ t->Union.tag_size = 0;
+ t->Union.variant_block_size = size;
+ } else {
+ // NOTE(bill): Align to tag
+ i64 tag_size = union_tag_size(t);
+ size = align_formula(max, tag_size);
+ // NOTE(bill): Calculate the padding between the common fields and the tag
+ t->Union.tag_size = tag_size;
+ t->Union.variant_block_size = size - field_size;
- return align_formula(size + tag_size, align);
+ size += tag_size;
+ }
+ return align_formula(size, align);
} break;