diff options
| author | Tyler Erickson <me@tylererickson.com> | 2020-03-05 16:49:53 -0800 |
|---|---|---|
| committer | Tyler Erickson <me@tylererickson.com> | 2020-03-05 16:49:53 -0800 |
| commit | bb026c99a93bc27ac89a8b1bf727b5b09799fe1c (patch) | |
| tree | 07a3707a3d0b665cfb03c4066d0c4552dc5b0cec /src | |
| parent | 2817bab494a093b861b33cba982d87d178e80501 (diff) | |
| parent | 2fe0eaf2adf952867d4ce4fba53b4b3ac75e1ba5 (diff) | |
Merged with master
Diffstat (limited to 'src')
| -rw-r--r-- | src/build_settings.cpp | 1 | ||||
| -rw-r--r-- | src/check_decl.cpp | 2 | ||||
| -rw-r--r-- | src/check_expr.cpp | 133 | ||||
| -rw-r--r-- | src/check_stmt.cpp | 14 | ||||
| -rw-r--r-- | src/check_type.cpp | 15 | ||||
| -rw-r--r-- | src/checker_builtin_procs.hpp | 4 | ||||
| -rw-r--r-- | src/entity.cpp | 1 | ||||
| -rw-r--r-- | src/ir.cpp | 79 | ||||
| -rw-r--r-- | src/ir_print.cpp | 44 | ||||
| -rw-r--r-- | src/main.cpp | 46 | ||||
| -rw-r--r-- | src/parser.cpp | 31 | ||||
| -rw-r--r-- | src/parser.hpp | 4 | ||||
| -rw-r--r-- | src/types.cpp | 51 |
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; |