From 0f399a72941c7cebcb5ad0580a9d94d1a7a37ac0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 1 Feb 2020 11:10:28 +0000 Subject: Add `union #maybe` --- src/check_expr.cpp | 98 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 37 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b854a693b..52c1f38b7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8572,8 +8572,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 +8592,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->mode = Addressing_OptionalOk; - } else if (is_type_any(src)) { - o->type = t; + o->type = bsrc->Union.variants[0]; 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"); -- cgit v1.2.3 From 85e331d5e21ed01feb03efc02cf6517fa9c3a20e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 23 Feb 2020 10:13:42 +0000 Subject: Fix #566 --- src/check_expr.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 52c1f38b7..722acaa24 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5942,7 +5942,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); -- cgit v1.2.3 From 8a6777514985793bc607ea1b915675a566033730 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 23 Feb 2020 10:20:12 +0000 Subject: Fix #571 --- src/check_expr.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 722acaa24..d98b3d0d6 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; } -- cgit v1.2.3 From 5073fcd39ee8693e8a8e781564f7c65184aec1b2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 23 Feb 2020 10:37:27 +0000 Subject: Improve error message on `using` with procedure parameters #568 --- src/check_decl.cpp | 2 +- src/check_expr.cpp | 7 +++++-- src/check_stmt.cpp | 8 +++++++- src/check_type.cpp | 4 ++-- 4 files changed, 15 insertions(+), 6 deletions(-) (limited to 'src/check_expr.cpp') 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 d98b3d0d6..069605035 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6300,7 +6300,7 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize } -bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array *clauses, bool print_err) { +bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, Array *clauses, bool print_err) { if (clauses != nullptr) { for_array(i, *clauses) { Ast *clause = (*clauses)[i]; @@ -6308,9 +6308,11 @@ bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array *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) { @@ -6352,6 +6354,7 @@ bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array *cla } } + if (call_expr) error(call_expr, "at caller location"); } return false; } @@ -6617,7 +6620,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; } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c365dca84..bef1919e4 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); diff --git a/src/check_type.cpp b/src/check_type.cpp index f21ffc956..97b9985c8 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, Arraywhere_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); } -- cgit v1.2.3 From 1596bca92d4d8b3457cbfacec24e2a2129bba40e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 26 Feb 2020 22:29:12 +0000 Subject: Add `intrinsics.cpu_relax` --- src/check_expr.cpp | 4 ++++ src/checker_builtin_procs.hpp | 4 ++++ src/ir.cpp | 12 ++++++++++++ src/ir_print.cpp | 12 ++++++++++++ 4 files changed, 32 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 069605035..b8fe76f6d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5280,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: 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/ir.cpp b/src/ir.cpp index e13530aca..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 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 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; @@ -6914,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: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a3f28c59f..bcbdca615 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1483,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) { -- cgit v1.2.3 From 3d74c2f6c0797345c2bdfae77c619057227e8181 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 26 Feb 2020 22:53:40 +0000 Subject: Add `proc(#const x: Type)` to enforce a constant parameter (but not polymorphic) to a procedure --- src/check_expr.cpp | 18 ++++++++++++++++++ src/check_type.cpp | 5 ++++- src/entity.cpp | 1 + src/parser.cpp | 8 ++++++++ src/parser.hpp | 3 ++- 5 files changed, 33 insertions(+), 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b8fe76f6d..1a01eef31 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5991,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)); @@ -6246,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; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 97b9985c8..6194951c9 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1722,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/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/parser.cpp b/src/parser.cpp index 2e2a1b97e..f89b5676b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2998,6 +2998,7 @@ enum FieldPrefixKind { FieldPrefix_Invalid = 0, FieldPrefix_using, + FieldPrefix_const, FieldPrefix_no_alias, FieldPrefix_c_var_arg, FieldPrefix_auto_cast, @@ -3024,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; } @@ -3036,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); @@ -3053,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; @@ -3066,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 cdfd4eba1..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, }; -- cgit v1.2.3