From 132fdf14b8c9dd717aea6ced64641030dd8adb47 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 3 Aug 2019 10:07:09 +0100 Subject: Fix `min`, `max`, `clamp` final type bug --- src/check_expr.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4c5ca2348..36304bf65 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3959,6 +3959,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Type *bt = base_type(operands[0].type); if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "min_f32"); if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "min_f64"); + + operand->type = operands[0].type; } } break; @@ -4119,6 +4121,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Type *bt = base_type(operands[0].type); if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "max_f32"); if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "max_f64"); + + operand->type = operands[0].type; } } break; @@ -4266,6 +4270,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 add_package_dependency(c, "runtime", "min_f64"); add_package_dependency(c, "runtime", "max_f64"); } + + operand->type = ops[0]->type; } } -- cgit v1.2.3 From 37633c1d2a9cc2ab5ae659ffb359f7f56f0457a2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 4 Aug 2019 11:02:00 +0100 Subject: `intrinsics.type_*` constant evaluation procedures --- src/check_expr.cpp | 412 +++++++++++++++++++++++++++++++++++++++++- src/checker.hpp | 214 +--------------------- src/checker_builtin_procs.hpp | 312 ++++++++++++++++++++++++++++++++ src/exact_value.cpp | 10 +- src/types.cpp | 13 ++ 5 files changed, 740 insertions(+), 221 deletions(-) create mode 100644 src/checker_builtin_procs.hpp (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 36304bf65..6a789bc33 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3178,7 +3178,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 /*fallthrough*/ } default: - if (ce->args.count > 0) { + if (BuiltinProc__type_begin < id && id < BuiltinProc__type_end) { + check_expr_or_type(c, operand, ce->args[0]); + } else if (ce->args.count > 0) { check_multi_expr(c, operand, ce->args[0]); } break; @@ -4445,6 +4447,414 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } break; + + case BuiltinProc_type_base_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + operand->type = base_type(operand->type); + } + operand->mode = Addressing_Type; + break; + case BuiltinProc_type_core_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + operand->type = core_type(operand->type); + } + operand->mode = Addressing_Type; + break; + + case BuiltinProc_type_is_boolean: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_boolean(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_integer: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_integer(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_rune: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_rune(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_float: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_complex: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_complex(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_string: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_string(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_typeid: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_typeid(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_any: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_any(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + + case BuiltinProc_type_is_endian_little: + operand->value = exact_value_bool(false); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected an integer type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_integer(operand->type)) { + error(operand->expr, "Expected an integer type for '%.*s'", LIT(builtin_name)); + } else { + operand->value = exact_value_bool(is_type_integer_endian_little(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_endian_big: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else if (!is_type_integer(operand->type)) { + error(operand->expr, "Expected an integer type for '%.*s'", LIT(builtin_name)); + } else { + operand->value = exact_value_bool(is_type_integer_endian_big(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_numeric: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_numeric(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_ordered: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_ordered(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_ordered_numeric: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_numeric(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_indexable: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_indexable(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_sliceable: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_sliceable(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_simple_compare: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_simple_compare(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_dereferenceable: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + if (is_type_rawptr(operand->type)) { + operand->value = exact_value_bool(false); + } else if (is_type_pointer(operand->type)) { + operand->value = exact_value_bool(true); + } else { + operand->value = exact_value_bool(false); + } + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + + case BuiltinProc_type_is_named: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_named(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_pointer: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_pointer(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_opaque: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_opaque(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_array: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_array(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_slice: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_slice(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_dynamic_array: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_dynamic_array(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_map: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_map(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_struct: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_struct(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_union: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_union(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_enum: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_enum(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_proc: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_proc(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_bit_field: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_bit_field(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_bit_field_value: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_bit_field_value(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_bit_set: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_bit_set(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + case BuiltinProc_type_is_simd_vector: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->value = exact_value_bool(false); + } else { + operand->value = exact_value_bool(is_type_simd_vector(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + + case BuiltinProc_type_has_nil: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + operand->value = exact_value_bool(type_has_nil(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + + case BuiltinProc_type_elem_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + switch (bt->kind) { + case Type_Basic: + switch (bt->Basic.kind) { + case Basic_complex64: operand->type = t_f32; break; + case Basic_complex128: operand->type = t_f64; break; + } + break; + case Type_Pointer: operand->type = bt->Pointer.elem; break; + case Type_Opaque: operand->type = bt->Opaque.elem; break; + case Type_Array: operand->type = bt->Array.elem; break; + case Type_Slice: operand->type = bt->Slice.elem; break; + case Type_DynamicArray: operand->type = bt->DynamicArray.elem; break; + } + } + operand->mode = Addressing_Type; + break; + + case BuiltinProc_type_proc_parameter_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + operand->value = exact_value_i64(bt->Proc.param_count); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; + case BuiltinProc_type_proc_return_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + operand->value = exact_value_i64(bt->Proc.result_count); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; } return true; diff --git a/src/checker.hpp b/src/checker.hpp index 66b68c35c..4e4a8cfe1 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -59,218 +59,8 @@ struct BuiltinProc { BuiltinProcPkg pkg; }; -enum BuiltinProcId { - BuiltinProc_Invalid, - - BuiltinProc_len, - BuiltinProc_cap, - - BuiltinProc_size_of, - BuiltinProc_align_of, - BuiltinProc_offset_of, - BuiltinProc_type_of, - BuiltinProc_type_info_of, - BuiltinProc_typeid_of, - - BuiltinProc_swizzle, - - BuiltinProc_complex, - BuiltinProc_real, - BuiltinProc_imag, - BuiltinProc_conj, - - BuiltinProc_expand_to_tuple, - - BuiltinProc_min, - BuiltinProc_max, - BuiltinProc_abs, - BuiltinProc_clamp, - - BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures - - // "Intrinsics" - BuiltinProc_vector, - - BuiltinProc_atomic_fence, - BuiltinProc_atomic_fence_acq, - BuiltinProc_atomic_fence_rel, - BuiltinProc_atomic_fence_acqrel, - - BuiltinProc_atomic_store, - BuiltinProc_atomic_store_rel, - BuiltinProc_atomic_store_relaxed, - BuiltinProc_atomic_store_unordered, - - BuiltinProc_atomic_load, - BuiltinProc_atomic_load_acq, - BuiltinProc_atomic_load_relaxed, - BuiltinProc_atomic_load_unordered, - - BuiltinProc_atomic_add, - BuiltinProc_atomic_add_acq, - BuiltinProc_atomic_add_rel, - BuiltinProc_atomic_add_acqrel, - BuiltinProc_atomic_add_relaxed, - BuiltinProc_atomic_sub, - BuiltinProc_atomic_sub_acq, - BuiltinProc_atomic_sub_rel, - BuiltinProc_atomic_sub_acqrel, - BuiltinProc_atomic_sub_relaxed, - BuiltinProc_atomic_and, - BuiltinProc_atomic_and_acq, - BuiltinProc_atomic_and_rel, - BuiltinProc_atomic_and_acqrel, - BuiltinProc_atomic_and_relaxed, - BuiltinProc_atomic_nand, - BuiltinProc_atomic_nand_acq, - BuiltinProc_atomic_nand_rel, - BuiltinProc_atomic_nand_acqrel, - BuiltinProc_atomic_nand_relaxed, - BuiltinProc_atomic_or, - BuiltinProc_atomic_or_acq, - BuiltinProc_atomic_or_rel, - BuiltinProc_atomic_or_acqrel, - BuiltinProc_atomic_or_relaxed, - BuiltinProc_atomic_xor, - BuiltinProc_atomic_xor_acq, - BuiltinProc_atomic_xor_rel, - BuiltinProc_atomic_xor_acqrel, - BuiltinProc_atomic_xor_relaxed, - - BuiltinProc_atomic_xchg, - BuiltinProc_atomic_xchg_acq, - BuiltinProc_atomic_xchg_rel, - BuiltinProc_atomic_xchg_acqrel, - BuiltinProc_atomic_xchg_relaxed, - - BuiltinProc_atomic_cxchg, - BuiltinProc_atomic_cxchg_acq, - BuiltinProc_atomic_cxchg_rel, - BuiltinProc_atomic_cxchg_acqrel, - BuiltinProc_atomic_cxchg_relaxed, - BuiltinProc_atomic_cxchg_failrelaxed, - BuiltinProc_atomic_cxchg_failacq, - BuiltinProc_atomic_cxchg_acq_failrelaxed, - BuiltinProc_atomic_cxchg_acqrel_failrelaxed, - - BuiltinProc_atomic_cxchgweak, - BuiltinProc_atomic_cxchgweak_acq, - BuiltinProc_atomic_cxchgweak_rel, - BuiltinProc_atomic_cxchgweak_acqrel, - BuiltinProc_atomic_cxchgweak_relaxed, - BuiltinProc_atomic_cxchgweak_failrelaxed, - BuiltinProc_atomic_cxchgweak_failacq, - BuiltinProc_atomic_cxchgweak_acq_failrelaxed, - BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed, - - BuiltinProc_COUNT, -}; -gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { - {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_builtin}, - - {STR_LIT("len"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("cap"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("size_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("align_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("offset_of"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("type_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("type_info_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("typeid_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("swizzle"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("complex"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("real"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("imag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("expand_to_tuple"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("min"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("max"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("abs"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("clamp"), 3, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE - - - // "Intrinsics" - {STR_LIT("vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type - - - {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}, - {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, -}; + +#include "checker_builtin_procs.hpp" // Operand is used as an intermediate value whilst checking diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp new file mode 100644 index 000000000..f6e3aee7e --- /dev/null +++ b/src/checker_builtin_procs.hpp @@ -0,0 +1,312 @@ +// checker_builtin_procs.hpp + +enum BuiltinProcId { + BuiltinProc_Invalid, + + BuiltinProc_len, + BuiltinProc_cap, + + BuiltinProc_size_of, + BuiltinProc_align_of, + BuiltinProc_offset_of, + BuiltinProc_type_of, + BuiltinProc_type_info_of, + BuiltinProc_typeid_of, + + BuiltinProc_swizzle, + + BuiltinProc_complex, + BuiltinProc_real, + BuiltinProc_imag, + BuiltinProc_conj, + + BuiltinProc_expand_to_tuple, + + BuiltinProc_min, + BuiltinProc_max, + BuiltinProc_abs, + BuiltinProc_clamp, + + BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures + + // "Intrinsics" + BuiltinProc_vector, + + BuiltinProc_atomic_fence, + BuiltinProc_atomic_fence_acq, + BuiltinProc_atomic_fence_rel, + BuiltinProc_atomic_fence_acqrel, + + BuiltinProc_atomic_store, + BuiltinProc_atomic_store_rel, + BuiltinProc_atomic_store_relaxed, + BuiltinProc_atomic_store_unordered, + + BuiltinProc_atomic_load, + BuiltinProc_atomic_load_acq, + BuiltinProc_atomic_load_relaxed, + BuiltinProc_atomic_load_unordered, + + BuiltinProc_atomic_add, + BuiltinProc_atomic_add_acq, + BuiltinProc_atomic_add_rel, + BuiltinProc_atomic_add_acqrel, + BuiltinProc_atomic_add_relaxed, + BuiltinProc_atomic_sub, + BuiltinProc_atomic_sub_acq, + BuiltinProc_atomic_sub_rel, + BuiltinProc_atomic_sub_acqrel, + BuiltinProc_atomic_sub_relaxed, + BuiltinProc_atomic_and, + BuiltinProc_atomic_and_acq, + BuiltinProc_atomic_and_rel, + BuiltinProc_atomic_and_acqrel, + BuiltinProc_atomic_and_relaxed, + BuiltinProc_atomic_nand, + BuiltinProc_atomic_nand_acq, + BuiltinProc_atomic_nand_rel, + BuiltinProc_atomic_nand_acqrel, + BuiltinProc_atomic_nand_relaxed, + BuiltinProc_atomic_or, + BuiltinProc_atomic_or_acq, + BuiltinProc_atomic_or_rel, + BuiltinProc_atomic_or_acqrel, + BuiltinProc_atomic_or_relaxed, + BuiltinProc_atomic_xor, + BuiltinProc_atomic_xor_acq, + BuiltinProc_atomic_xor_rel, + BuiltinProc_atomic_xor_acqrel, + BuiltinProc_atomic_xor_relaxed, + + BuiltinProc_atomic_xchg, + BuiltinProc_atomic_xchg_acq, + BuiltinProc_atomic_xchg_rel, + BuiltinProc_atomic_xchg_acqrel, + BuiltinProc_atomic_xchg_relaxed, + + BuiltinProc_atomic_cxchg, + BuiltinProc_atomic_cxchg_acq, + BuiltinProc_atomic_cxchg_rel, + BuiltinProc_atomic_cxchg_acqrel, + BuiltinProc_atomic_cxchg_relaxed, + BuiltinProc_atomic_cxchg_failrelaxed, + BuiltinProc_atomic_cxchg_failacq, + BuiltinProc_atomic_cxchg_acq_failrelaxed, + BuiltinProc_atomic_cxchg_acqrel_failrelaxed, + + BuiltinProc_atomic_cxchgweak, + BuiltinProc_atomic_cxchgweak_acq, + BuiltinProc_atomic_cxchgweak_rel, + BuiltinProc_atomic_cxchgweak_acqrel, + BuiltinProc_atomic_cxchgweak_relaxed, + BuiltinProc_atomic_cxchgweak_failrelaxed, + BuiltinProc_atomic_cxchgweak_failacq, + BuiltinProc_atomic_cxchgweak_acq_failrelaxed, + BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed, + + + // Constant type tests +BuiltinProc__type_begin, + + BuiltinProc_type_base_type, + BuiltinProc_type_core_type, + + BuiltinProc_type_is_boolean, + BuiltinProc_type_is_integer, + BuiltinProc_type_is_rune, + BuiltinProc_type_is_float, + BuiltinProc_type_is_complex, + BuiltinProc_type_is_string, + BuiltinProc_type_is_typeid, + BuiltinProc_type_is_any, + + BuiltinProc_type_is_endian_little, + BuiltinProc_type_is_endian_big, + BuiltinProc_type_is_numeric, + BuiltinProc_type_is_ordered, + BuiltinProc_type_is_ordered_numeric, + BuiltinProc_type_is_indexable, + BuiltinProc_type_is_sliceable, + BuiltinProc_type_is_simple_compare, // easily compared using memcmp + BuiltinProc_type_is_dereferenceable, + + BuiltinProc_type_is_named, + BuiltinProc_type_is_pointer, + BuiltinProc_type_is_opaque, + BuiltinProc_type_is_array, + BuiltinProc_type_is_slice, + BuiltinProc_type_is_dynamic_array, + BuiltinProc_type_is_map, + BuiltinProc_type_is_struct, + BuiltinProc_type_is_union, + BuiltinProc_type_is_enum, + BuiltinProc_type_is_proc, + BuiltinProc_type_is_bit_field, + BuiltinProc_type_is_bit_field_value, + BuiltinProc_type_is_bit_set, + BuiltinProc_type_is_simd_vector, + + BuiltinProc_type_has_nil, + + BuiltinProc_type_elem_type, + + BuiltinProc_type_proc_parameter_count, + BuiltinProc_type_proc_return_count, + +BuiltinProc__type_end, + + BuiltinProc_COUNT, +}; +gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_builtin}, + + {STR_LIT("len"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("cap"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT("size_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("align_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("offset_of"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("type_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("type_info_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("typeid_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT("swizzle"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT("complex"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("real"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("imag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT("expand_to_tuple"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT("min"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("max"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("abs"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("clamp"), 3, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE + + + // "Intrinsics" + {STR_LIT("vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type + + {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}, + {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_base_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_core_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_is_boolean"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_integer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_rune"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_float"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_complex"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_string"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_typeid"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_is_endian_little"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_endian_big"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_numeric"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_ordered_numeric"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_indexable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_sliceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_simple_compare"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics,}, + {STR_LIT("type_is_dereferenceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_is_named"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_opaque"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_slice"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_dynamic_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_map"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_struct"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_union"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_bit_field_value"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_has_nil"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_elem_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_proc_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_proc_return_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, +}; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 428690291..7d822512b 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -53,14 +53,8 @@ HashKey hash_exact_value(ExactValue v) { return hash_integer(u64(v.value_bool)); case ExactValue_String: return hash_string(v.value_string); - case ExactValue_Integer: { - u64 *d = big_int_ptr(&v.value_integer); - u64 x = 0; - for (i32 i = 0; i < v.value_integer.len; i++) { - x |= d[i]; - } - return hash_integer(x); - } + case ExactValue_Integer: + return hashing_proc(big_int_ptr(&v.value_integer), v.value_integer.len * gb_size_of(u64)); case ExactValue_Float: return hash_f64(v.value_float); case ExactValue_Pointer: diff --git a/src/types.cpp b/src/types.cpp index 5cf86d6b6..76126f17f 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1307,6 +1307,19 @@ bool is_type_indexable(Type *t) { return false; } +bool is_type_sliceable(Type *t) { + Type *bt = base_type(t); + switch (bt->kind) { + case Type_Basic: + return bt->Basic.kind == Basic_string; + case Type_Array: + case Type_Slice: + case Type_DynamicArray: + return true; + } + return false; +} + bool is_type_polymorphic_record(Type *t) { t = base_type(t); -- cgit v1.2.3 From b04231dd95b6a80789b5e8762a51db4506948da1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 4 Aug 2019 14:54:23 +0100 Subject: Improve implementation of `intrinsics.type_*` procedures --- src/check_expr.cpp | 401 +++++++++--------------------------------- src/checker_builtin_procs.hpp | 6 +- src/types.cpp | 20 ++- 3 files changed, 98 insertions(+), 329 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6a789bc33..99fe6695b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3131,6 +3131,55 @@ bool check_identifier_exists(Scope *s, Ast *node, bool nested = false, Scope **o return false; } +typedef bool (BuiltinTypeIsProc)(Type *t); + +BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_end - BuiltinProc__type_begin - 1] = { + nullptr, // BuiltinProc_type_base_type + nullptr, // BuiltinProc_type_core_type + nullptr, // BuiltinProc_type_elem_type + + is_type_boolean, + is_type_integer, + is_type_rune, + is_type_float, + is_type_complex, + is_type_string, + is_type_typeid, + is_type_any, + is_type_endian_little, + is_type_endian_big, + is_type_numeric, + is_type_ordered, + is_type_ordered_numeric, + is_type_indexable, + is_type_sliceable, + is_type_simple_compare, + is_type_dereferenceable, + + is_type_named, + is_type_pointer, + is_type_opaque, + is_type_array, + is_type_slice, + is_type_dynamic_array, + + is_type_map, + is_type_struct, + is_type_union, + is_type_enum, + is_type_proc, + is_type_bit_field, + is_type_bit_field_value, + is_type_bit_set, + is_type_simd_vector, + + type_has_nil, + + nullptr, // BuiltinProc_type_proc_parameter_count + nullptr, // BuiltinProc_type_proc_return_count +}; + + bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id) { ast_node(ce, CallExpr, call); @@ -4464,371 +4513,78 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } operand->mode = Addressing_Type; break; - - case BuiltinProc_type_is_boolean: + case BuiltinProc_type_elem_type: if (operand->mode != Addressing_Type) { error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); } else { - operand->value = exact_value_bool(is_type_boolean(operand->type)); + Type *bt = base_type(operand->type); + switch (bt->kind) { + case Type_Basic: + switch (bt->Basic.kind) { + case Basic_complex64: operand->type = t_f32; break; + case Basic_complex128: operand->type = t_f64; break; + } + break; + case Type_Pointer: operand->type = bt->Pointer.elem; break; + case Type_Opaque: operand->type = bt->Opaque.elem; break; + case Type_Array: operand->type = bt->Array.elem; break; + case Type_Slice: operand->type = bt->Slice.elem; break; + case Type_DynamicArray: operand->type = bt->DynamicArray.elem; break; + } } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; + operand->mode = Addressing_Type; break; + + + case BuiltinProc_type_is_boolean: case BuiltinProc_type_is_integer: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_integer(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_rune: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_rune(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_float: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - } else { - - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_complex: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_complex(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_string: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_string(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_typeid: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_typeid(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_any: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_any(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; - case BuiltinProc_type_is_endian_little: - operand->value = exact_value_bool(false); - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected an integer type for '%.*s'", LIT(builtin_name)); - } else if (!is_type_integer(operand->type)) { - error(operand->expr, "Expected an integer type for '%.*s'", LIT(builtin_name)); - } else { - operand->value = exact_value_bool(is_type_integer_endian_little(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_endian_big: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else if (!is_type_integer(operand->type)) { - error(operand->expr, "Expected an integer type for '%.*s'", LIT(builtin_name)); - } else { - operand->value = exact_value_bool(is_type_integer_endian_big(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_numeric: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_numeric(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_ordered: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_ordered(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_ordered_numeric: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_numeric(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_indexable: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_indexable(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_sliceable: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_sliceable(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_simple_compare: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_simple_compare(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_dereferenceable: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - if (is_type_rawptr(operand->type)) { - operand->value = exact_value_bool(false); - } else if (is_type_pointer(operand->type)) { - operand->value = exact_value_bool(true); - } else { - operand->value = exact_value_bool(false); - } - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; - case BuiltinProc_type_is_named: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_named(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_pointer: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_pointer(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_opaque: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_opaque(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_array: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_array(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_slice: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_slice(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_dynamic_array: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_dynamic_array(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_map: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_map(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_struct: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_struct(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_union: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_union(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_enum: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_enum(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_proc: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_proc(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_bit_field: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_bit_field(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_bit_field_value: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_bit_field_value(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_bit_set: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_bit_set(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; case BuiltinProc_type_is_simd_vector: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - operand->value = exact_value_bool(false); - } else { - operand->value = exact_value_bool(is_type_simd_vector(operand->type)); - } - operand->mode = Addressing_Constant; - operand->type = t_untyped_bool; - break; - case BuiltinProc_type_has_nil: + GB_ASSERT(BuiltinProc__type_begin < id && id < BuiltinProc__type_end); + operand->value = exact_value_bool(false); if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + gbString str = expr_to_string(ce->args[0]); + error(operand->expr, "Expected a type for '%.*s', got '%s'", LIT(builtin_name), str); + gb_string_free(str); } else { - operand->value = exact_value_bool(type_has_nil(operand->type)); + i32 i = id - (BuiltinProc__type_begin+1); + auto procedure = builtin_type_is_procs[i]; + GB_ASSERT_MSG(procedure != nullptr, "%.*s", LIT(builtin_name)); + operand->value = exact_value_bool(procedure(operand->type)); } operand->mode = Addressing_Constant; operand->type = t_untyped_bool; break; - case BuiltinProc_type_elem_type: - if (operand->mode != Addressing_Type) { - error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); - } else { - Type *bt = base_type(operand->type); - switch (bt->kind) { - case Type_Basic: - switch (bt->Basic.kind) { - case Basic_complex64: operand->type = t_f32; break; - case Basic_complex128: operand->type = t_f64; break; - } - break; - case Type_Pointer: operand->type = bt->Pointer.elem; break; - case Type_Opaque: operand->type = bt->Opaque.elem; break; - case Type_Array: operand->type = bt->Array.elem; break; - case Type_Slice: operand->type = bt->Slice.elem; break; - case Type_DynamicArray: operand->type = bt->DynamicArray.elem; break; - } - } - operand->mode = Addressing_Type; - break; - case BuiltinProc_type_proc_parameter_count: operand->value = exact_value_i64(0); if (operand->mode != Addressing_Type) { @@ -7527,6 +7283,9 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case Ast_UnionType: case Ast_EnumType: case Ast_MapType: + case Ast_OpaqueType: + case Ast_BitSetType: + case Ast_BitFieldType: o->mode = Addressing_Type; o->type = check_type(c, node); break; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index f6e3aee7e..536d47616 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -110,6 +110,7 @@ BuiltinProc__type_begin, BuiltinProc_type_base_type, BuiltinProc_type_core_type, + BuiltinProc_type_elem_type, BuiltinProc_type_is_boolean, BuiltinProc_type_is_integer, @@ -148,8 +149,6 @@ BuiltinProc__type_begin, BuiltinProc_type_has_nil, - BuiltinProc_type_elem_type, - BuiltinProc_type_proc_parameter_count, BuiltinProc_type_proc_return_count, @@ -266,6 +265,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("type_base_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_core_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_elem_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_boolean"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_integer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -304,8 +304,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_has_nil"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("type_elem_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("type_proc_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_proc_return_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, diff --git a/src/types.cpp b/src/types.cpp index 76126f17f..e5d2509e0 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1122,12 +1122,11 @@ bool is_type_integer_endian_big(Type *t) { return is_type_integer_endian_big(bit_set_to_int(t)); } else if (t->kind == Type_Pointer) { return is_type_integer_endian_big(&basic_types[Basic_uintptr]); - } else { - GB_PANIC("Unsupported type: %s", type_to_string(t)); } return build_context.endian_kind == TargetEndian_Big; } + bool is_type_integer_endian_little(Type *t) { t = core_type(t); if (t->kind == Type_Basic) { @@ -1141,11 +1140,24 @@ bool is_type_integer_endian_little(Type *t) { return is_type_integer_endian_little(bit_set_to_int(t)); } else if (t->kind == Type_Pointer) { return is_type_integer_endian_little(&basic_types[Basic_uintptr]); - } else { - GB_PANIC("Unsupported type: %s", type_to_string(t)); } return build_context.endian_kind == TargetEndian_Little; } +bool is_type_endian_big(Type *t) { + return is_type_integer_endian_big(t); +} +bool is_type_endian_little(Type *t) { + return is_type_integer_endian_little(t); +} + +bool is_type_dereferenceable(Type *t) { + if (is_type_rawptr(t)) { + return false; + } + return is_type_pointer(t); +} + + bool is_type_different_to_arch_endianness(Type *t) { switch (build_context.endian_kind) { -- cgit v1.2.3 From 04036aba9c1eec05fe143d939ef93e017502f015 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 11 Aug 2019 23:58:49 +0100 Subject: `package reflect`; fix substring type bug; fix scoping rules for `using` on procedure parameter --- core/reflect/reflect.odin | 285 ++++++++++++++++++++++++++++++++++++++++++++++ core/runtime/core.odin | 11 +- examples/demo/demo.odin | 34 ++++-- src/check_decl.cpp | 118 ++++++++++++------- src/check_expr.cpp | 2 +- 5 files changed, 392 insertions(+), 58 deletions(-) create mode 100644 core/reflect/reflect.odin (limited to 'src/check_expr.cpp') diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin new file mode 100644 index 000000000..ecc2e7362 --- /dev/null +++ b/core/reflect/reflect.odin @@ -0,0 +1,285 @@ +package reflect + +import "core:runtime" +import "core:mem" + + +Type_Kind :: enum { + Invalid, + + Named, + Integer, + Rune, + Float, + Complex, + String, + Boolean, + Any, + Type_Id, + Pointer, + Procedure, + Array, + Dynamic_Array, + Slice, + Tuple, + Struct, + Union, + Enum, + Map, + Bit_Field, + Bit_Set, + Opaque, + Simd_Vector, +} + + +type_kind :: proc(T: typeid) -> Type_Kind { + ti := type_info_of(T); + if ti != nil { + #complete switch _ in ti.variant { + case runtime.Type_Info_Named: return .Named; + case runtime.Type_Info_Integer: return .Integer; + case runtime.Type_Info_Rune: return .Rune; + case runtime.Type_Info_Float: return .Float; + case runtime.Type_Info_Complex: return .Complex; + case runtime.Type_Info_String: return .String; + case runtime.Type_Info_Boolean: return .Boolean; + case runtime.Type_Info_Any: return .Any; + case runtime.Type_Info_Type_Id: return .Type_Id; + case runtime.Type_Info_Pointer: return .Pointer; + case runtime.Type_Info_Procedure: return .Procedure; + case runtime.Type_Info_Array: return .Array; + case runtime.Type_Info_Dynamic_Array: return .Dynamic_Array; + case runtime.Type_Info_Slice: return .Slice; + case runtime.Type_Info_Tuple: return .Tuple; + case runtime.Type_Info_Struct: return .Struct; + case runtime.Type_Info_Union: return .Union; + case runtime.Type_Info_Enum: return .Enum; + case runtime.Type_Info_Map: return .Map; + case runtime.Type_Info_Bit_Field: return .Bit_Field; + case runtime.Type_Info_Bit_Set: return .Bit_Set; + case runtime.Type_Info_Opaque: return .Opaque; + case runtime.Type_Info_Simd_Vector: return .Simd_Vector; + } + + } + return .Invalid; +} + +// TODO(bill): Better name +underlying_type_kind :: proc(T: typeid) -> Type_Kind { + return type_kind(runtime.typeid_base(T)); +} + +// TODO(bill): Better name +backing_type_kind :: proc(T: typeid) -> Type_Kind { + return type_kind(runtime.typeid_core(T)); +} + + + +size_of_typeid :: proc(T: typeid) -> int { + if ti := type_info_of(T); ti != nil { + return ti.size; + } + return 0; +} + +align_of_typeid :: proc(T: typeid) -> int { + if ti := type_info_of(T); ti != nil { + return ti.align; + } + return 1; +} + +to_bytes :: proc(v: any) -> []byte { + if v != nil { + sz := size_of_typeid(v.id); + return mem.slice_ptr((^byte)(v.data), sz); + } + return nil; +} + +any_data :: inline proc(v: any) -> (data: rawptr, id: typeid) { + return v.data, v.id; +} + +is_nil :: proc(v: any) -> bool { + data := to_bytes(v); + if data != nil { + return true; + } + for v in data do if v != 0 { + return false; + } + return true; +} + + +index :: proc(v: any, i: int, loc := #caller_location) -> any { + if v == nil do return nil; + + v := v; + v.id = runtime.typeid_base(v.id); + switch a in v { + case runtime.Type_Info_Array: + runtime.bounds_check_error_loc(loc, i, a.count); + offset := uintptr(a.elem.size * i); + data := rawptr(uintptr(v.data) + offset); + return any{data, a.elem.id}; + + case runtime.Type_Info_Slice: + raw := (^mem.Raw_Slice)(v.data); + runtime.bounds_check_error_loc(loc, i, raw.len); + offset := uintptr(a.elem.size * i); + data := rawptr(uintptr(raw.data) + offset); + return any{data, a.elem.id}; + + case runtime.Type_Info_Dynamic_Array: + raw := (^mem.Raw_Dynamic_Array)(v.data); + runtime.bounds_check_error_loc(loc, i, raw.len); + offset := uintptr(a.elem.size * i); + data := rawptr(uintptr(raw.data) + offset); + return any{data, a.elem.id}; + + case runtime.Type_Info_String: + if a.is_cstring do return nil; + + raw := (^mem.Raw_String)(v.data); + runtime.bounds_check_error_loc(loc, i, raw.len); + offset := uintptr(size_of(u8) * i); + data := rawptr(uintptr(raw.data) + offset); + return any{data, typeid_of(u8)}; + } + return nil; +} + + + + +Struct_Tag :: distinct string; + +Struct_Field :: struct { + name: string, + type: typeid, + tag: Struct_Tag, + offset: uintptr, +} + +struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) { + ti := runtime.type_info_base(type_info_of(T)); + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + if 0 <= i && i < len(s.names) { + field.name = s.names[i]; + field.type = s.types[i].id; + field.tag = Struct_Tag(s.tags[i]); + field.offset = s.offsets[i]; + } + } + return; +} + +struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) { + ti := runtime.type_info_base(type_info_of(T)); + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + for fname, i in s.names { + if fname == name { + field.name = s.names[i]; + field.type = s.types[i].id; + field.tag = Struct_Tag(s.tags[i]); + field.offset = s.offsets[i]; + break; + } + } + } + return; +} + + + +struct_field_names :: proc(T: typeid) -> []string { + ti := runtime.type_info_base(type_info_of(T)); + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + return s.names; + } + return nil; +} + +struct_field_types :: proc(T: typeid) -> []^runtime.Type_Info { + ti := runtime.type_info_base(type_info_of(T)); + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + return s.types; + } + return nil; +} + + +struct_field_tags :: proc(T: typeid) -> []Struct_Tag { + ti := runtime.type_info_base(type_info_of(T)); + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + return transmute([]Struct_Tag)s.tags; + } + return nil; +} + +struct_field_offsets :: proc(T: typeid) -> []uintptr { + ti := runtime.type_info_base(type_info_of(T)); + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + return s.offsets; + } + return nil; +} + + + +struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: string) { + value, _ = struct_tag_lookup(tag, key); + return; +} + +struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: string, ok: bool) { + for tag := tag; tag != ""; /**/ { + i := 0; + for i < len(tag) && tag[i] == ' ' { // Skip whitespace + i += 1; + } + tag = tag[i:]; + if len(tag) == 0 do break; + + i = 0; + loop: for i < len(tag) { + switch tag[i] { + case ':', '"': + break loop; + case 0x00 ..< ' ', 0x7f .. 0x9f: // break if control character is found + break loop; + } + i += 1; + } + + if i == 0 do break; + if i+1 >= len(tag) do break; + + if tag[i] != ':' || tag[i+1] != '"' { + break; + } + name := string(tag[:i]); + tag = tag[i+1:]; + + i = 1; + for i < len(tag) && tag[i] != '"' { // find closing quote + if tag[i] == '\\' do i += 1; // Skip escaped characters + i += 1; + } + + if i >= len(tag) do break; + + val := string(tag[:i+1]); + tag = tag[i+1:]; + + if key == name { + return val[1:i], true; + } + } + return; +} diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 28767cc2d..047281b10 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -283,19 +283,21 @@ type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info { } -type_info_base_without_enum :: proc "contextless" (info: ^Type_Info) -> ^Type_Info { +type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info { if info == nil do return nil; base := info; loop: for { switch i in base.variant { - case Type_Info_Named: base = i.base; - case Type_Info_Enum: base = i.base; + case Type_Info_Named: base = i.base; + case Type_Info_Enum: base = i.base; + case Type_Info_Opaque: base = i.elem; case: break loop; } } return base; } +type_info_base_without_enum :: type_info_core; __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info { data := transmute(Typeid_Bit_Field)id; @@ -311,10 +313,11 @@ typeid_base :: proc "contextless" (id: typeid) -> typeid { ti = type_info_base(ti); return ti.id; } -typeid_base_without_enum :: proc "contextless" (id: typeid) -> typeid { +typeid_core :: proc "contextless" (id: typeid) -> typeid { ti := type_info_base_without_enum(type_info_of(id)); return ti.id; } +typeid_base_without_enum :: typeid_core; diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index e07eea7c7..157ecb9bf 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -4,6 +4,7 @@ import "core:fmt" import "core:mem" import "core:os" import "core:runtime" +import "core:reflect" when os.OS == "windows" { import "core:thread" @@ -946,26 +947,39 @@ deferred_procedure_associations :: proc() { } } -struct_field_tags :: proc() { - fmt.println("# struct_field_tags"); +reflection :: proc() { + fmt.println("# reflection"); Foo :: struct { x: int `tag1`, - y: string `tag2`, + y: string `json:"y_field"`, z: bool, // no tag } - ti := runtime.type_info_base(type_info_of(Foo)); - s := ti.variant.(runtime.Type_Info_Struct); + id := typeid_of(Foo); + names := reflect.struct_field_names(id); + types := reflect.struct_field_types(id); + tags := reflect.struct_field_tags(id); + + assert(len(names) == len(types) && len(names) == len(tags)); + fmt.println("Foo :: struct {"); - for _, i in s.names { - if tag := s.tags[i]; tag != "" { - fmt.printf("\t%s: %T `%s`,\n", s.names[i], s.types[i], tag); + for tag, i in tags { + name, type := names[i], types[i]; + if tag != "" { + fmt.printf("\t%s: %T `%s`,\n", name, type, tag); } else { - fmt.printf("\t%s: %T,\n", s.names[i], s.types[i]); + fmt.printf("\t%s: %T,\n", name, type); } } fmt.println("}"); + + + for tag, i in tags { + if val, ok := reflect.struct_tag_lookup(tag, "json"); ok { + fmt.printf("json: %s -> %s\n", names[i], val); + } + } } main :: proc() { @@ -986,6 +1000,6 @@ main :: proc() { bit_set_type(); diverging_procedures(); deferred_procedure_associations(); - struct_field_tags(); + reflection(); } } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f3e0f70bc..cf3cb8e97 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1074,6 +1074,11 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_ } +struct ProcUsingVar { + Entity *e; + Entity *uvar; +}; + void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *type, Ast *body) { if (body == nullptr) { @@ -1098,58 +1103,85 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty ctx->curr_proc_decl = decl; ctx->curr_proc_sig = type; - GB_ASSERT(type->kind == Type_Proc); - if (type->Proc.param_count > 0) { - TypeTuple *params = &type->Proc.params->Tuple; - for_array(i, params->variables) { - Entity *e = params->variables[i]; - if (e->kind != Entity_Variable) { - continue; - } - if (!(e->flags & EntityFlag_Using)) { - continue; - } - bool is_immutable = e->Variable.is_immutable; - bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type); - String name = e->token.string; - Type *t = base_type(type_deref(e->type)); - if (t->kind == Type_Struct) { - Scope *scope = t->Struct.scope; - if (scope == nullptr) { - scope = scope_of_node(t->Struct.node); + ast_node(bs, BlockStmt, body); + + Array using_entities = {}; + using_entities.allocator = heap_allocator(); + defer (array_free(&using_entities)); + + { + GB_ASSERT(type->kind == Type_Proc); + if (type->Proc.param_count > 0) { + TypeTuple *params = &type->Proc.params->Tuple; + for_array(i, params->variables) { + Entity *e = params->variables[i]; + if (e->kind != Entity_Variable) { + continue; } - GB_ASSERT(scope != nullptr); - for_array(i, scope->elements.entries) { - Entity *f = scope->elements.entries[i].value; - if (f->kind == Entity_Variable) { - Entity *uvar = alloc_entity_using_variable(e, f->token, f->type); - uvar->Variable.is_immutable = is_immutable; - if (is_value) uvar->flags |= EntityFlag_Value; - - Entity *prev = scope_insert(ctx->scope, uvar); - if (prev != nullptr) { - error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(name), LIT(prev->token.string)); - break; + if (!(e->flags & EntityFlag_Using)) { + continue; + } + bool is_immutable = e->Variable.is_immutable; + bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type); + String name = e->token.string; + Type *t = base_type(type_deref(e->type)); + if (t->kind == Type_Struct) { + Scope *scope = t->Struct.scope; + if (scope == nullptr) { + scope = scope_of_node(t->Struct.node); + } + GB_ASSERT(scope != nullptr); + for_array(i, scope->elements.entries) { + Entity *f = scope->elements.entries[i].value; + if (f->kind == Entity_Variable) { + Entity *uvar = alloc_entity_using_variable(e, f->token, f->type); + uvar->Variable.is_immutable = is_immutable; + if (is_value) uvar->flags |= EntityFlag_Value; + + + ProcUsingVar puv = {e, uvar}; + array_add(&using_entities, puv); + } } + } else { + error(e->token, "'using' can only be applied to variables of type struct"); + break; } - } else { - error(e->token, "'using' can only be applied to variables of type struct"); - break; } } } - ast_node(bs, BlockStmt, body); + + for_array(i, using_entities) { + Entity *e = using_entities[i].e; + Entity *uvar = using_entities[i].uvar; + Entity *prev = scope_insert(ctx->scope, uvar); + if (prev != nullptr) { + error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string)); + break; + } + } + check_open_scope(ctx, body); - check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls); - if (type->Proc.result_count > 0) { - if (!check_is_terminating(body)) { - if (token.kind == Token_Ident) { - error(bs->close, "Missing return statement at the end of the procedure '%.*s'", LIT(token.string)); - } else { - // NOTE(bill): Anonymous procedure (lambda) - error(bs->close, "Missing return statement at the end of the procedure"); + { + for_array(i, using_entities) { + Entity *e = using_entities[i].e; + Entity *uvar = using_entities[i].uvar; + Entity *prev = scope_insert(ctx->scope, uvar); + // NOTE(bill): Don't err here + } + + check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls); + + if (type->Proc.result_count > 0) { + if (!check_is_terminating(body)) { + if (token.kind == Token_Ident) { + error(bs->close, "Missing return statement at the end of the procedure '%.*s'", LIT(token.string)); + } else { + // NOTE(bill): Anonymous procedure (lambda) + error(bs->close, "Missing return statement at the end of the procedure"); + } } } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 99fe6695b..7a6e0f22d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7164,7 +7164,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (o->mode == Addressing_Constant) { max_count = o->value.value_string.len; } - o->type = t_string; + o->type = type_deref(o->type); } break; -- cgit v1.2.3 From b86dfa7af79a1563b2f47622aeeaedb737eaafb8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 13 Aug 2019 22:51:04 +0100 Subject: Fix compiler crash with #defined #417 --- src/check_expr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7a6e0f22d..8da6816ac 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3368,8 +3368,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } Ast *arg = unparen_expr(ce->args[0]); - if (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr) { - error(call, "'#defined' expects an identifier or selector expression, got %s", LIT(ast_strings[arg->kind])); + if (arg == nullptr || (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr)) { + error(call, "'#defined' expects an identifier or selector expression, got %.*s", LIT(ast_strings[arg->kind])); return false; } -- cgit v1.2.3 From 150d4e343ddd99eef1d921cdd494c108ac2e9a12 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 23 Aug 2019 10:24:18 +0100 Subject: Fix `~(1 << x)` type inference bug --- src/check_expr.cpp | 17 ++++++++++------- src/check_stmt.cpp | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8da6816ac..6c252b9b6 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1767,7 +1767,7 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { } -void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) { +void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *type_hint) { GB_ASSERT(node->kind == Ast_BinaryExpr); ast_node(be, BinaryExpr, node); @@ -1845,6 +1845,9 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) { info->is_lhs = true; } x->mode = Addressing_Value; + if (type_hint && is_type_integer(type_hint)) { + x->type = type_hint; + } // x->value = x_val; return; } @@ -2167,7 +2170,7 @@ bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x, Operand *y } -void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as_type_hint=false) { +void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint, bool use_lhs_as_type_hint=false) { GB_ASSERT(node->kind == Ast_BinaryExpr); Operand y_ = {}, *y = &y_; @@ -2178,7 +2181,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as case Token_CmpEq: case Token_NotEq: { // NOTE(bill): Allow comparisons between types - check_expr_or_type(c, x, be->left); + check_expr_or_type(c, x, be->left, type_hint); check_expr_or_type(c, y, be->right, x->type); bool xt = x->mode == Addressing_Type; bool yt = y->mode == Addressing_Type; @@ -2278,11 +2281,11 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as return; default: - check_expr(c, x, be->left); + check_expr_with_type_hint(c, x, be->left, type_hint); if (use_lhs_as_type_hint) { check_expr_with_type_hint(c, y, be->right, x->type); } else { - check_expr(c, y, be->right); + check_expr_with_type_hint(c, y, be->right, type_hint); } break; } @@ -2307,7 +2310,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as } if (token_is_shift(op.kind)) { - check_shift(c, x, y, node); + check_shift(c, x, y, node, type_hint); return; } @@ -7000,7 +7003,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(be, BinaryExpr, node); - check_binary_expr(c, o, node, true); + check_binary_expr(c, o, node, type_hint, true); if (o->mode == Addressing_Invalid) { o->expr = node; return kind; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index e2090688f..2787aa436 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1168,7 +1168,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { be->right = as->rhs[0]; check_expr(ctx, &lhs, as->lhs[0]); - check_binary_expr(ctx, &rhs, &binary_expr, true); + check_binary_expr(ctx, &rhs, &binary_expr, nullptr, true); if (rhs.mode == Addressing_Invalid) { return; } -- cgit v1.2.3 From cf23954297993b3e574c295f95f75b294fdca4ec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 23 Aug 2019 11:51:04 +0100 Subject: Improve #assert to show the procedure and signature it was called with; Allow the ability to print ExactValue correct now. --- src/check_expr.cpp | 9 +++++- src/exact_value.cpp | 46 +++++++++++++++++++++++++++++ src/string.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/types.cpp | 65 ++++++++++++++++++++++++++--------------- 4 files changed, 179 insertions(+), 25 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6c252b9b6..24a130d62 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3360,6 +3360,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (!operand->value.value_bool) { gbString arg = expr_to_string(ce->args[0]); error(call, "Compile time assertion: %s", arg); + if (c->proc_name != "") { + gbString str = type_to_string(c->curr_proc_sig); + error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str); + gb_string_free(str); + } gb_string_free(arg); } @@ -3744,7 +3749,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } if (x.mode == Addressing_Constant && y.mode == Addressing_Constant) { - operand->value = exact_binary_operator_value(Token_Add, x.value, y.value); + f64 r = exact_value_to_float(x.value).value_float; + f64 i = exact_value_to_float(y.value).value_float; + operand->value = exact_value_complex(r, i); operand->mode = Addressing_Constant; } else { operand->mode = Addressing_Value; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 7d822512b..be034ecc5 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -718,3 +718,49 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { GB_PANIC("Invalid comparison"); return false; } + + +gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize string_limit=36) { + switch (v.kind) { + case ExactValue_Invalid: + return str; + case ExactValue_Bool: + return gb_string_appendc(str, v.value_bool ? "true" : "false"); + case ExactValue_String: { + String s = quote_to_ascii(heap_allocator(), v.value_string); + string_limit = gb_max(string_limit, 36); + if (s.len <= string_limit) { + str = gb_string_append_length(str, s.text, s.len); + } else { + isize n = string_limit/5; + str = gb_string_append_length(str, s.text, n); + str = gb_string_append_fmt(str, "\"..%lld chars..\"", s.len-(2*n)); + str = gb_string_append_length(str, s.text+s.len-n, n); + } + gb_free(heap_allocator(), s.text); + return str; + } + case ExactValue_Integer: { + String s = big_int_to_string(heap_allocator(), &v.value_integer); + str = gb_string_append_length(str, s.text, s.len); + gb_free(heap_allocator(), s.text); + return str; + } + case ExactValue_Float: + return gb_string_append_fmt(str, "%f", v.value_float); + case ExactValue_Complex: + return gb_string_append_fmt(str, "%f+%fi", v.value_complex.real, v.value_complex.imag); + + case ExactValue_Pointer: + return str; + case ExactValue_Compound: + return str; + case ExactValue_Procedure: + return str; + } + return str; +}; + +gbString exact_value_to_string(ExactValue const &v, isize string_limit=35) { + return write_exact_value_to_string(gb_string_make(heap_allocator(), ""), v, string_limit); +} diff --git a/src/string.cpp b/src/string.cpp index 774061edf..a29f3bd77 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -440,12 +440,94 @@ String string16_to_string(gbAllocator a, String16 s) { +bool is_printable(Rune r) { + if (r <= 0xff) { + if (0x20 <= r && r <= 0x7e) { + return true; + } + if (0xa1 <= r && r <= 0xff) { + return r != 0xad; + } + return false; + } + return false; +} +gb_global char const lower_hex[] = "0123456789abcdef"; + +String quote_to_ascii(gbAllocator a, String str, u8 quote='"') { + u8 *s = str.text; + isize n = str.len; + auto buf = array_make(a, 0, n); + array_add(&buf, quote); + for (isize width = 0; n > 0; s += width, n -= width) { + Rune r = cast(Rune)s[0]; + width = 1; + if (r >= 0x80) { + width = gb_utf8_decode(s, n, &r); + } + if (width == 1 && r == GB_RUNE_INVALID) { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'x'); + array_add(&buf, cast(u8)lower_hex[s[0]>>4]); + array_add(&buf, cast(u8)lower_hex[s[0]&0xf]); + continue; + } + if (r == quote || r == '\\') { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, u8(r)); + continue; + } + if (r < 0x80 && is_printable(r)) { + array_add(&buf, u8(r)); + continue; + } + switch (r) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + default: + if (r < ' ') { + u8 b = cast(u8)r; + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'x'); + array_add(&buf, cast(u8)lower_hex[b>>4]); + array_add(&buf, cast(u8)lower_hex[b&0xf]); + } + if (r > GB_RUNE_MAX) { + r = 0XFFFD; + } + if (r < 0x10000) { + u8 b = cast(u8)r; + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'u'); + for (isize i = 12; i >= 0; i -= 4) { + array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]); + } + } else { + u8 b = cast(u8)r; + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'U'); + for (isize i = 28; i >= 0; i -= 4) { + array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]); + } + } + } + } - + array_add(&buf, quote); + String res = {}; + res.text = buf.data; + res.len = buf.count; + return res; +} diff --git a/src/types.cpp b/src/types.cpp index 4404882a9..b5c9152b6 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2968,35 +2968,54 @@ gbString write_type_to_string(gbString str, Type *type) { isize comma_index = 0; for_array(i, type->Tuple.variables) { Entity *var = type->Tuple.variables[i]; - if (var != nullptr) { - if (var->kind == Entity_Constant) { - // Ignore - continue; + if (var == nullptr) { + continue; + } + String name = var->token.string; + if (var->kind == Entity_Constant) { + str = gb_string_appendc(str, "$"); + str = gb_string_append_length(str, name.text, name.len); + if (!is_type_untyped(var->type)) { + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, var->type); + str = gb_string_appendc(str, " = "); + str = write_exact_value_to_string(str, var->Constant.value); + } else { + str = gb_string_appendc(str, "="); + str = write_exact_value_to_string(str, var->Constant.value); } + continue; + } - if (comma_index++ > 0) { - str = gb_string_appendc(str, ", "); - } + if (comma_index++ > 0) { + str = gb_string_appendc(str, ", "); + } - if (var->kind == Entity_Variable) { - if (var->flags&EntityFlag_CVarArg) { - str = gb_string_appendc(str, "#c_vararg "); - } - if (var->flags&EntityFlag_Ellipsis) { - Type *slice = base_type(var->type); - str = gb_string_appendc(str, ".."); - GB_ASSERT(var->type->kind == Type_Slice); - str = write_type_to_string(str, slice->Slice.elem); - } else { - str = write_type_to_string(str, var->type); - } + if (var->kind == Entity_Variable) { + if (var->flags&EntityFlag_CVarArg) { + str = gb_string_appendc(str, "#c_vararg "); + } + if (var->flags&EntityFlag_Ellipsis) { + Type *slice = base_type(var->type); + str = gb_string_appendc(str, ".."); + GB_ASSERT(var->type->kind == Type_Slice); + str = write_type_to_string(str, slice->Slice.elem); + } else { + str = write_type_to_string(str, var->type); + } + } else { + GB_ASSERT(var->kind == Entity_TypeName); + if (var->type->kind == Type_Generic) { + str = gb_string_appendc(str, "typeid/"); + str = write_type_to_string(str, var->type); } else { - GB_ASSERT(var->kind == Entity_TypeName); - if (var->type->kind == Type_Generic) { - str = gb_string_appendc(str, "type/"); + if (var->kind == Entity_TypeName) { + str = gb_string_appendc(str, "$"); + str = gb_string_append_length(str, name.text, name.len); + str = gb_string_appendc(str, "="); str = write_type_to_string(str, var->type); } else { - str = gb_string_appendc(str, "type"); + str = gb_string_appendc(str, "typeid"); } } } -- cgit v1.2.3 From 7bc146e6fde909298a7184fdf00cec91868ffc00 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Aug 2019 11:33:05 +0100 Subject: Built-in Quaternions (Not just an April Fool's Joke any more) --- core/fmt/fmt.odin | 29 +++++ core/reflect/reflect.odin | 2 + core/runtime/core.odin | 23 ++-- core/runtime/internal.odin | 70 +++++++++++- examples/demo/demo.odin | 60 +++++++++-- src/check_expr.cpp | 242 +++++++++++++++++++++++++++++++++++++++-- src/checker.cpp | 6 ++ src/checker_builtin_procs.hpp | 6 ++ src/exact_value.cpp | 184 +++++++++++++++++++++++++++++-- src/ir.cpp | 245 ++++++++++++++++++++++++++++++++++++++++-- src/ir_print.cpp | 53 +++++++-- src/tokenizer.cpp | 5 +- src/types.cpp | 50 +++++++-- 13 files changed, 909 insertions(+), 66 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 5dbebf972..a2f1166ac 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1129,6 +1129,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Rune: fmt_arg(fi, v, verb); case runtime.Type_Info_Float: fmt_arg(fi, v, verb); case runtime.Type_Info_Complex: fmt_arg(fi, v, verb); + case runtime.Type_Info_Quaternion: fmt_arg(fi, v, verb); case runtime.Type_Info_String: fmt_arg(fi, v, verb); case runtime.Type_Info_Pointer: @@ -1386,6 +1387,31 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { } } +fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { + switch verb { + case 'f', 'F', 'v', 'h', 'H': + r, i, j, k := real(q), imag(q), jmag(q), kmag(q); + + fmt_float(fi, r, bits/4, verb); + + if !fi.plus && i >= 0 do strings.write_rune(fi.buf, '+'); + fmt_float(fi, i, bits/4, verb); + strings.write_rune(fi.buf, 'i'); + + if !fi.plus && j >= 0 do strings.write_rune(fi.buf, '+'); + fmt_float(fi, j, bits/4, verb); + strings.write_rune(fi.buf, 'j'); + + if !fi.plus && k >= 0 do strings.write_rune(fi.buf, '+'); + fmt_float(fi, k, bits/4, verb); + strings.write_rune(fi.buf, 'k'); + + case: + fmt_bad_verb(fi, verb); + return; + } +} + fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { if arg == nil { strings.write_string(fi.buf, ""); @@ -1434,6 +1460,9 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { case complex64: fmt_complex(fi, complex128(a), 64, verb); case complex128: fmt_complex(fi, a, 128, verb); + case quaternion128: fmt_quaternion(fi, quaternion256(a), 128, verb); + case quaternion256: fmt_quaternion(fi, a, 256, verb); + case i8: fmt_int(fi, u64(a), true, 8, verb); case u8: fmt_int(fi, u64(a), false, 8, verb); case i16: fmt_int(fi, u64(a), true, 16, verb); diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index a4450490f..4bc9a2225 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -12,6 +12,7 @@ Type_Kind :: enum { Rune, Float, Complex, + Quaternion, String, Boolean, Any, @@ -42,6 +43,7 @@ type_kind :: proc(T: typeid) -> Type_Kind { case runtime.Type_Info_Rune: return .Rune; case runtime.Type_Info_Float: return .Float; case runtime.Type_Info_Complex: return .Complex; + case runtime.Type_Info_Quaternion: return .Quaternion; case runtime.Type_Info_String: return .String; case runtime.Type_Info_Boolean: return .Boolean; case runtime.Type_Info_Any: return .Any; diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 047281b10..022a84e9e 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -47,16 +47,17 @@ Type_Info_Endianness :: enum u8 { } // Variant Types -Type_Info_Named :: struct {name: string, base: ^Type_Info}; -Type_Info_Integer :: struct {signed: bool, endianness: Type_Info_Endianness}; -Type_Info_Rune :: struct {}; -Type_Info_Float :: struct {}; -Type_Info_Complex :: struct {}; -Type_Info_String :: struct {is_cstring: bool}; -Type_Info_Boolean :: struct {}; -Type_Info_Any :: struct {}; -Type_Info_Type_Id :: struct {}; -Type_Info_Pointer :: struct { +Type_Info_Named :: struct {name: string, base: ^Type_Info}; +Type_Info_Integer :: struct {signed: bool, endianness: Type_Info_Endianness}; +Type_Info_Rune :: struct {}; +Type_Info_Float :: struct {}; +Type_Info_Complex :: struct {}; +Type_Info_Quaternion :: struct {}; +Type_Info_String :: struct {is_cstring: bool}; +Type_Info_Boolean :: struct {}; +Type_Info_Any :: struct {}; +Type_Info_Type_Id :: struct {}; +Type_Info_Pointer :: struct { elem: ^Type_Info // nil -> rawptr }; Type_Info_Procedure :: struct { @@ -135,6 +136,7 @@ Type_Info :: struct { Type_Info_Rune, Type_Info_Float, Type_Info_Complex, + Type_Info_Quaternion, Type_Info_String, Type_Info_Boolean, Type_Info_Any, @@ -163,6 +165,7 @@ Typeid_Kind :: enum u8 { Rune, Float, Complex, + Quaternion, String, Boolean, Any, diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index c33d5f62c..a5099944b 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -357,6 +357,11 @@ complex128_eq :: inline proc "contextless" (a, b: complex128) -> bool { return r complex128_ne :: inline proc "contextless" (a, b: complex128) -> bool { return real(a) != real(b) || imag(a) != imag(b); } +quaternion128_eq :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); } +quaternion128_ne :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); } + +quaternion256_eq :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); } +quaternion256_ne :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); } bounds_check_error :: proc "contextless" (file: string, line, column: int, index, count: int) { @@ -546,9 +551,16 @@ abs_complex128 :: inline proc "contextless" (x: complex128) -> f64 { r, i := real(x), imag(x); return _sqrt_f64(r*r + i*i); } +abs_quaternion128 :: inline proc "contextless" (x: quaternion128) -> f32 { + r, i, j, k := real(x), imag(x), jmag(x), kmag(x); + return _sqrt_f32(r*r + i*i + j*j + k*k); +} +abs_quaternion256 :: inline proc "contextless" (x: quaternion256) -> f64 { + r, i, j, k := real(x), imag(x), jmag(x), kmag(x); + return _sqrt_f64(r*r + i*i + j*j + k*k); +} - -quo_complex64 :: proc(n, m: complex64) -> complex64 { +quo_complex64 :: proc "contextless" (n, m: complex64) -> complex64 { e, f: f32; if abs(real(m)) >= abs(imag(m)) { @@ -566,7 +578,7 @@ quo_complex64 :: proc(n, m: complex64) -> complex64 { return complex(e, f); } -quo_complex128 :: proc(n, m: complex128) -> complex128 { +quo_complex128 :: proc "contextless" (n, m: complex128) -> complex128 { e, f: f64; if abs(real(m)) >= abs(imag(m)) { @@ -583,3 +595,55 @@ quo_complex128 :: proc(n, m: complex128) -> complex128 { return complex(e, f); } + +mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 { + q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q); + r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r); + + t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3; + t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2; + t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1; + t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0; + + return quaternion(t0, t1, t2, t3); +} + +mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 { + q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q); + r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r); + + t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3; + t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2; + t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1; + t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0; + + return quaternion(t0, t1, t2, t3); +} + +quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 { + q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q); + r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r); + + invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3); + + t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2; + t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2; + t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2; + t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2; + + return quaternion(t0, t1, t2, t3); +} + +quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 { + q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q); + r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r); + + invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3); + + t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2; + t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2; + t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2; + t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2; + + return quaternion(t0, t1, t2, t3); +} diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 343c50ff7..a96056b61 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -326,7 +326,7 @@ union_type :: proc() { } parametric_polymorphism :: proc() { - fmt.println("# parametric_polymorphism"); + fmt.println("\n# parametric_polymorphism"); print_value :: proc(value: $T) { fmt.printf("print_value: %T %v\n", value, value); @@ -561,7 +561,7 @@ prefix_table := [?]string{ threading_example :: proc() { when os.OS == "windows" { - fmt.println("# threading_example"); + fmt.println("\n# threading_example"); worker_proc :: proc(t: ^thread.Thread) -> int { for iteration in 1..5 { @@ -601,7 +601,7 @@ threading_example :: proc() { } array_programming :: proc() { - fmt.println("# array_programming"); + fmt.println("\n# array_programming"); { a := [3]f32{1, 2, 3}; b := [3]f32{5, 6, 7}; @@ -646,7 +646,7 @@ array_programming :: proc() { } named_proc_return_parameters :: proc() { - fmt.println("# named proc return parameters"); + fmt.println("\n# named proc return parameters"); foo0 :: proc() -> int { return 123; @@ -668,7 +668,7 @@ named_proc_return_parameters :: proc() { using_enum :: proc() { - fmt.println("# using enum"); + fmt.println("\n# using enum"); using Foo :: enum {A, B, C}; @@ -680,7 +680,7 @@ using_enum :: proc() { } map_type :: proc() { - fmt.println("# map type"); + fmt.println("\n# map type"); // enums of type u16, u32, i16 & i32 also work Enum_u8 :: enum u8 { @@ -735,7 +735,7 @@ map_type :: proc() { } implicit_selector_expression :: proc() { - fmt.println("# implicit selector expression"); + fmt.println("\n# implicit selector expression"); Foo :: enum {A, B, C}; @@ -763,7 +763,7 @@ implicit_selector_expression :: proc() { } explicit_procedure_overloading :: proc() { - fmt.println("# explicit procedure overloading"); + fmt.println("\n# explicit procedure overloading"); add_ints :: proc(a, b: int) -> int { x := a + b; @@ -797,7 +797,7 @@ explicit_procedure_overloading :: proc() { } complete_switch :: proc() { - fmt.println("# complete_switch"); + fmt.println("\n# complete_switch"); { // enum using Foo :: enum { A, @@ -947,7 +947,7 @@ deferred_procedure_associations :: proc() { } reflection :: proc() { - fmt.println("# reflection"); + fmt.println("\n# reflection"); Foo :: struct { x: int `tag1`, @@ -981,6 +981,45 @@ reflection :: proc() { } } +quaternions :: proc() { + fmt.println("\n# quaternions"); + + { // Quaternion operations + q := 1 + 2i + 3j + 4k; + r := quaternion(5, 6, 7, 8); + t := q * r; + fmt.printf("(%v) * (%v) = %v\n", q, r, t); + v := q / r; + fmt.printf("(%v) / (%v) = %v\n", q, r, v); + u := q + r; + fmt.printf("(%v) + (%v) = %v\n", q, r, u); + s := q - r; + fmt.printf("(%v) - (%v) = %v\n", q, r, s); + } + { // The quaternion types + q128: quaternion128; // 4xf32 + q256: quaternion256; // 4xf64 + q128 = quaternion(1, 0, 0, 0); + q256 = 1; // quaternion(1, 0, 0, 0); + } + { // Built-in procedures + q := 1 + 2i + 3j + 4k; + fmt.println("q =", q); + fmt.println("real(q) =", real(q)); + fmt.println("imag(q) =", imag(q)); + fmt.println("jmag(q) =", jmag(q)); + fmt.println("kmag(q) =", kmag(q)); + fmt.println("conj(q) =", conj(q)); + fmt.println("abs(q) =", abs(q)); + } + { // Conversion of a complex type to a quaternion type + c := 1 + 2i; + q := quaternion256(c); + fmt.println(c); + fmt.println(q); + } +} + main :: proc() { when true { general_stuff(); @@ -1000,5 +1039,6 @@ main :: proc() { diverging_procedures(); deferred_procedure_associations(); reflection(); + quaternions(); } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 24a130d62..6a9a53275 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -497,6 +497,14 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type if (is_type_complex(dst)) { return 1; } + if (is_type_quaternion(dst)) { + return 2; + } + break; + case Basic_UntypedQuaternion: + if (is_type_quaternion(dst)) { + return 1; + } break; } } @@ -1438,7 +1446,7 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ ExactValue imag = exact_value_imag(v); if (real.kind != ExactValue_Invalid && imag.kind != ExactValue_Invalid) { - if (out_value) *out_value = exact_binary_operator_value(Token_Add, real, exact_value_make_imag(imag)); + if (out_value) *out_value = exact_value_complex(exact_value_to_f64(real), exact_value_to_f64(imag)); return true; } break; @@ -1449,6 +1457,36 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ default: GB_PANIC("Compiler error: Unknown complex type!"); break; } + return false; + } else if (is_type_quaternion(type)) { + ExactValue v = exact_value_to_quaternion(in_value); + if (v.kind != ExactValue_Quaternion) { + return false; + } + + switch (type->Basic.kind) { + case Basic_quaternion128: + case Basic_quaternion256: { + ExactValue real = exact_value_real(v); + ExactValue imag = exact_value_imag(v); + ExactValue jmag = exact_value_jmag(v); + ExactValue kmag = exact_value_kmag(v); + if (real.kind != ExactValue_Invalid && + imag.kind != ExactValue_Invalid) { + if (out_value) *out_value = exact_value_quaternion(exact_value_to_f64(real), exact_value_to_f64(imag), exact_value_to_f64(jmag), exact_value_to_f64(kmag)); + return true; + } + break; + } + case Basic_UntypedComplex: + if (out_value) *out_value = exact_value_to_quaternion(*out_value); + return true; + case Basic_UntypedQuaternion: + return true; + + default: GB_PANIC("Compiler error: Unknown complex type!"); break; + } + return false; } else if (is_type_pointer(type)) { if (in_value.kind == ExactValue_Pointer) { @@ -1481,10 +1519,14 @@ void check_is_expressible(CheckerContext *c, Operand *o, Type *type) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s'", a, b); } else { + #if 0 gbAllocator ha = heap_allocator(); String str = big_int_to_string(ha, &o->value.value_integer); defer (gb_free(ha, str.text)); error(o->expr, "'%s = %.*s' overflows '%s'", a, LIT(str), b); + #else + error(o->expr, "Cannot convert '%s' to '%s'", a, b); + #endif } } else { error(o->expr, "Cannot convert '%s' to '%s'", a, b); @@ -1759,6 +1801,21 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { } break; } + } else if (is_type_quaternion(x->type) || is_type_quaternion(y->type)) { + switch (op) { + case Token_CmpEq: + switch (8*size) { + case 128: add_package_dependency(c, "runtime", "quaternion128_eq"); break; + case 256: add_package_dependency(c, "runtime", "quaternion256_eq"); break; + } + break; + case Token_NotEq: + switch (8*size) { + case 128: add_package_dependency(c, "runtime", "quaternion128_ne"); break; + case 256: add_package_dependency(c, "runtime", "quaternion256_ne"); break; + } + break; + } } } @@ -1978,6 +2035,14 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) { return true; } + if (is_type_complex(src) && is_type_quaternion(dst)) { + return true; + } + + if (is_type_quaternion(src) && is_type_quaternion(dst)) { + return true; + } + if (is_type_bit_field_value(src) && is_type_integer(dst)) { return true; } @@ -2557,6 +2622,8 @@ ExactValue convert_exact_value_for_type(ExactValue v, Type *type) { v = exact_value_to_integer(v); } else if (is_type_complex(t)) { v = exact_value_to_complex(v); + } else if (is_type_quaternion(t)) { + v = exact_value_to_quaternion(v); } return v; } @@ -2652,6 +2719,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { case Basic_UntypedInteger: case Basic_UntypedFloat: case Basic_UntypedComplex: + case Basic_UntypedQuaternion: case Basic_UntypedRune: if (!is_type_numeric(target_type)) { operand->mode = Addressing_Invalid; @@ -3769,10 +3837,98 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } + case BuiltinProc_quaternion: { + // quaternion :: proc(real, imag, jmag, kmag: float_type) -> complex_type + Operand x = *operand; + Operand y = {}; + Operand z = {}; + Operand w = {}; + + // NOTE(bill): Invalid will be the default till fixed + operand->type = t_invalid; + operand->mode = Addressing_Invalid; + + check_expr(c, &y, ce->args[1]); + if (y.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &z, ce->args[2]); + if (y.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &w, ce->args[3]); + if (y.mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false; + convert_to_typed(c, &w, x.type); if (w.mode == Addressing_Invalid) return false; + if (x.mode == Addressing_Constant && + y.mode == Addressing_Constant && + z.mode == Addressing_Constant && + w.mode == Addressing_Constant) { + if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) { + x.type = t_untyped_float; + } + if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) { + y.type = t_untyped_float; + } + if (is_type_numeric(z.type) && exact_value_imag(z.value).value_float == 0) { + z.type = t_untyped_float; + } + if (is_type_numeric(w.type) && exact_value_imag(w.value).value_float == 0) { + w.type = t_untyped_float; + } + } + + if (!(are_types_identical(x.type, y.type) && are_types_identical(x.type, z.type) && are_types_identical(x.type, w.type))) { + gbString tx = type_to_string(x.type); + gbString ty = type_to_string(y.type); + gbString tz = type_to_string(z.type); + gbString tw = type_to_string(w.type); + error(call, "Mismatched types to 'quaternion', '%s' vs '%s' vs '%s' vs '%s'", tx, ty, tz, tw); + gb_string_free(tw); + gb_string_free(tz); + gb_string_free(ty); + gb_string_free(tx); + return false; + } + + if (!is_type_float(x.type)) { + gbString s = type_to_string(x.type); + error(call, "Arguments have type '%s', expected a floating point", s); + gb_string_free(s); + return false; + } + + if (x.mode == Addressing_Constant && y.mode == Addressing_Constant && z.mode == Addressing_Constant && w.mode == Addressing_Constant) { + f64 r = exact_value_to_float(x.value).value_float; + f64 i = exact_value_to_float(y.value).value_float; + f64 j = exact_value_to_float(z.value).value_float; + f64 k = exact_value_to_float(w.value).value_float; + operand->value = exact_value_quaternion(r, i, j, k); + operand->mode = Addressing_Constant; + } else { + operand->mode = Addressing_Value; + } + + BasicKind kind = core_type(x.type)->Basic.kind; + switch (kind) { + case Basic_f32: operand->type = t_quaternion128; break; + case Basic_f64: operand->type = t_quaternion256; break; + case Basic_UntypedFloat: operand->type = t_untyped_quaternion; break; + default: GB_PANIC("Invalid type"); break; + } + + break; + } + case BuiltinProc_real: case BuiltinProc_imag: { // real :: proc(x: type) -> float_type - // proc imag(x: type) -> float_type + // imag :: proc(x: type) -> float_type Operand *x = operand; if (is_type_untyped(x->type)) { @@ -3780,7 +3936,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (is_type_numeric(x->type)) { x->type = t_untyped_complex; } - } else { + } else if (is_type_quaternion(x->type)) { + convert_to_typed(c, x, t_quaternion256); + if (x->mode == Addressing_Invalid) { + return false; + } + } else{ convert_to_typed(c, x, t_complex128); if (x->mode == Addressing_Invalid) { return false; @@ -3788,9 +3949,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } } - if (!is_type_complex(x->type)) { + if (!is_type_complex(x->type) && !is_type_quaternion(x->type)) { gbString s = type_to_string(x->type); - error(call, "Argument has type '%s', expected a complex type", s); + error(call, "Argument has type '%s', expected a complex or quaternion type", s); gb_string_free(s); return false; } @@ -3808,7 +3969,57 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 switch (kind) { case Basic_complex64: x->type = t_f32; break; case Basic_complex128: x->type = t_f64; break; + case Basic_quaternion128: x->type = t_f32; break; + case Basic_quaternion256: x->type = t_f64; break; case Basic_UntypedComplex: x->type = t_untyped_float; break; + case Basic_UntypedQuaternion: x->type = t_untyped_float; break; + default: GB_PANIC("Invalid type"); break; + } + + break; + } + + case BuiltinProc_jmag: + case BuiltinProc_kmag: { + // jmag :: proc(x: type) -> float_type + // kmag :: proc(x: type) -> float_type + + Operand *x = operand; + if (is_type_untyped(x->type)) { + if (x->mode == Addressing_Constant) { + if (is_type_numeric(x->type)) { + x->type = t_untyped_complex; + } + } else{ + convert_to_typed(c, x, t_quaternion256); + if (x->mode == Addressing_Invalid) { + return false; + } + } + } + + if (!is_type_quaternion(x->type)) { + gbString s = type_to_string(x->type); + error(call, "Argument has type '%s', expected a quaternion type", s); + gb_string_free(s); + return false; + } + + if (x->mode == Addressing_Constant) { + switch (id) { + case BuiltinProc_jmag: x->value = exact_value_jmag(x->value); break; + case BuiltinProc_kmag: x->value = exact_value_kmag(x->value); break; + } + } else { + x->mode = Addressing_Value; + } + + BasicKind kind = core_type(x->type)->Basic.kind; + switch (kind) { + case Basic_quaternion128: x->type = t_f32; break; + case Basic_quaternion256: x->type = t_f64; break; + case Basic_UntypedComplex: x->type = t_untyped_float; break; + case Basic_UntypedQuaternion: x->type = t_untyped_float; break; default: GB_PANIC("Invalid type"); break; } @@ -3822,12 +4033,24 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (x->mode == Addressing_Constant) { ExactValue v = exact_value_to_complex(x->value); f64 r = v.value_complex.real; - f64 i = v.value_complex.imag; + f64 i = -v.value_complex.imag; x->value = exact_value_complex(r, i); x->mode = Addressing_Constant; } else { x->mode = Addressing_Value; } + } else if (is_type_quaternion(x->type)) { + if (x->mode == Addressing_Constant) { + ExactValue v = exact_value_to_quaternion(x->value); + f64 r = v.value_quaternion.real; + f64 i = -v.value_quaternion.imag; + f64 j = -v.value_quaternion.jmag; + f64 k = -v.value_quaternion.kmag; + x->value = exact_value_quaternion(r, i, j, k); + x->mode = Addressing_Constant; + } else { + x->mode = Addressing_Value; + } } else { gbString s = type_to_string(x->type); error(call, "Expected a complex or quaternion, got '%s'", s); @@ -4226,6 +4449,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "abs_f64"); if (are_types_identical(bt, t_complex64)) add_package_dependency(c, "runtime", "abs_complex64"); if (are_types_identical(bt, t_complex128)) add_package_dependency(c, "runtime", "abs_complex128"); + if (are_types_identical(bt, t_quaternion128)) add_package_dependency(c, "runtime", "abs_quaternion128"); + if (are_types_identical(bt, t_quaternion256)) add_package_dependency(c, "runtime", "abs_quaternion256"); } } @@ -6232,8 +6457,11 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case Token_Imag: { String s = bl->token.string; Rune r = s[s.len-1]; + // NOTE(bill, 2019-08-25): Allow for quaternions by having j and k imaginary numbers switch (r) { - case 'i': t = t_untyped_complex; break; + case 'i': t = t_untyped_complex; break; + case 'j': t = t_untyped_quaternion; break; + case 'k': t = t_untyped_quaternion; break; } break; diff --git a/src/checker.cpp b/src/checker.cpp index 45b661a58..d6f5ad402 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1610,6 +1610,10 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("quo_complex64"), str_lit("quo_complex128"), + str_lit("mul_quaternion128"), + str_lit("mul_quaternion256"), + str_lit("quo_quaternion128"), + str_lit("quo_quaternion256"), str_lit("umodti3"), str_lit("udivti3"), @@ -1891,6 +1895,7 @@ void init_core_type_info(Checker *c) { t_type_info_integer = find_core_type(c, str_lit("Type_Info_Integer")); t_type_info_rune = find_core_type(c, str_lit("Type_Info_Rune")); t_type_info_float = find_core_type(c, str_lit("Type_Info_Float")); + t_type_info_quaternion = find_core_type(c, str_lit("Type_Info_Quaternion")); t_type_info_complex = find_core_type(c, str_lit("Type_Info_Complex")); t_type_info_string = find_core_type(c, str_lit("Type_Info_String")); t_type_info_boolean = find_core_type(c, str_lit("Type_Info_Boolean")); @@ -1915,6 +1920,7 @@ void init_core_type_info(Checker *c) { t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer); t_type_info_rune_ptr = alloc_type_pointer(t_type_info_rune); t_type_info_float_ptr = alloc_type_pointer(t_type_info_float); + t_type_info_quaternion_ptr = alloc_type_pointer(t_type_info_quaternion); t_type_info_complex_ptr = alloc_type_pointer(t_type_info_complex); t_type_info_string_ptr = alloc_type_pointer(t_type_info_string); t_type_info_boolean_ptr = alloc_type_pointer(t_type_info_boolean); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 536d47616..0135b3c50 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -16,8 +16,11 @@ enum BuiltinProcId { BuiltinProc_swizzle, BuiltinProc_complex, + BuiltinProc_quaternion, BuiltinProc_real, BuiltinProc_imag, + BuiltinProc_jmag, + BuiltinProc_kmag, BuiltinProc_conj, BuiltinProc_expand_to_tuple, @@ -172,8 +175,11 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("swizzle"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("complex"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("quaternion"), 4, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("real"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("imag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("jmag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("kmag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("expand_to_tuple"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 520812e44..54d4ef6f2 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -12,6 +12,19 @@ bool are_types_identical(Type *x, Type *y); struct Complex128 { f64 real, imag; }; +struct Quaternion256 { + f64 imag, jmag, kmag, real; +}; + +Quaternion256 quaternion256_inverse(Quaternion256 x) { + f64 invmag2 = 1.0 / (x.real*x.real + x.imag*x.imag + x.jmag*x.jmag + x.kmag*x.kmag); + x.real = +x.real * invmag2; + x.imag = -x.imag * invmag2; + x.jmag = -x.jmag * invmag2; + x.kmag = -x.kmag * invmag2; + return x; +} + enum ExactValueKind { ExactValue_Invalid, @@ -21,6 +34,7 @@ enum ExactValueKind { ExactValue_Integer, ExactValue_Float, ExactValue_Complex, + ExactValue_Quaternion, ExactValue_Pointer, ExactValue_Compound, // TODO(bill): Is this good enough? ExactValue_Procedure, // TODO(bill): Is this good enough? @@ -37,6 +51,7 @@ struct ExactValue { f64 value_float; i64 value_pointer; Complex128 value_complex; + Quaternion256 value_quaternion; Ast * value_compound; Ast * value_procedure; }; @@ -61,7 +76,8 @@ HashKey hash_exact_value(ExactValue v) { return hash_integer(v.value_pointer); case ExactValue_Complex: return hashing_proc(&v.value_complex, gb_size_of(Complex128)); - + case ExactValue_Quaternion: + return hashing_proc(&v.value_quaternion, gb_size_of(Quaternion256)); case ExactValue_Compound: return hash_pointer(v.value_compound); case ExactValue_Procedure: @@ -116,6 +132,15 @@ ExactValue exact_value_complex(f64 real, f64 imag) { return result; } +ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) { + ExactValue result = {ExactValue_Quaternion}; + result.value_quaternion.real = real; + result.value_quaternion.imag = imag; + result.value_quaternion.jmag = jmag; + result.value_quaternion.kmag = kmag; + return result; +} + ExactValue exact_value_pointer(i64 ptr) { ExactValue result = {ExactValue_Pointer}; result.value_pointer = ptr; @@ -253,8 +278,10 @@ ExactValue exact_value_from_basic_literal(Token token) { str.len--; // Ignore the 'i|j|k' f64 imag = float_from_string(str); - if (last_rune == 'i') { - return exact_value_complex(0, imag); + switch (last_rune) { + case 'i': return exact_value_complex(0, imag); + case 'j': return exact_value_quaternion(0, 0, imag, 0); + case 'k': return exact_value_quaternion(0, 0, 0, imag); } } case Token_Rune: { @@ -318,11 +345,26 @@ ExactValue exact_value_to_complex(ExactValue v) { return exact_value_complex(v.value_float, 0); case ExactValue_Complex: return v; + // case ExactValue_Quaternion: + // return exact_value_complex(v.value_quaternion.real, v.value_quaternion.imag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} +ExactValue exact_value_to_quaternion(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_quaternion(big_int_to_f64(&v.value_integer), 0, 0, 0); + case ExactValue_Float: + return exact_value_quaternion(v.value_float, 0, 0, 0); + case ExactValue_Complex: + return exact_value_quaternion(v.value_complex.real, v.value_complex.imag, 0, 0); + case ExactValue_Quaternion: + return v; } ExactValue r = {ExactValue_Invalid}; return r; } - ExactValue exact_value_real(ExactValue v) { switch (v.kind) { @@ -331,6 +373,8 @@ ExactValue exact_value_real(ExactValue v) { return v; case ExactValue_Complex: return exact_value_float(v.value_complex.real); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.real); } ExactValue r = {ExactValue_Invalid}; return r; @@ -343,6 +387,34 @@ ExactValue exact_value_imag(ExactValue v) { return exact_value_i64(0); case ExactValue_Complex: return exact_value_float(v.value_complex.imag); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.imag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_jmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + case ExactValue_Float: + case ExactValue_Complex: + return exact_value_i64(0); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.jmag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_kmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + case ExactValue_Float: + case ExactValue_Complex: + return exact_value_i64(0); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.kmag); } ExactValue r = {ExactValue_Invalid}; return r; @@ -361,6 +433,32 @@ ExactValue exact_value_make_imag(ExactValue v) { return r; } +ExactValue exact_value_make_jmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_quaternion(0, 0, exact_value_to_float(v).value_float, 0); + case ExactValue_Float: + return exact_value_quaternion(0, 0, v.value_float, 0); + default: + GB_PANIC("Expected an integer or float type for 'exact_value_make_imag'"); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_make_kmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_quaternion(0, 0, 0, exact_value_to_float(v).value_float); + case ExactValue_Float: + return exact_value_quaternion(0, 0, 0, v.value_float); + default: + GB_PANIC("Expected an integer or float type for 'exact_value_make_imag'"); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + i64 exact_value_to_i64(ExactValue v) { v = exact_value_to_integer(v); if (v.kind == ExactValue_Integer) { @@ -389,6 +487,7 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, case ExactValue_Integer: case ExactValue_Float: case ExactValue_Complex: + case ExactValue_Quaternion: return v; } break; @@ -413,6 +512,13 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, f64 imag = v.value_complex.imag; return exact_value_complex(-real, -imag); } + case ExactValue_Quaternion: { + f64 real = v.value_quaternion.real; + f64 imag = v.value_quaternion.imag; + f64 jmag = v.value_quaternion.jmag; + f64 kmag = v.value_quaternion.kmag; + return exact_value_quaternion(-real, -imag, -jmag, -kmag); + } } break; } @@ -463,8 +569,10 @@ i32 exact_value_order(ExactValue const &v) { return 3; case ExactValue_Complex: return 4; - case ExactValue_Pointer: + case ExactValue_Quaternion: return 5; + case ExactValue_Pointer: + return 6; default: GB_PANIC("How'd you get here? Invalid Value.kind"); @@ -485,7 +593,7 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Bool: case ExactValue_String: - case ExactValue_Complex: + case ExactValue_Quaternion: return; case ExactValue_Integer: @@ -499,6 +607,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Complex: *x = exact_value_complex(big_int_to_f64(&x->value_integer), 0); return; + case ExactValue_Quaternion: + *x = exact_value_quaternion(big_int_to_f64(&x->value_integer), 0, 0, 0); + return; } break; @@ -509,6 +620,17 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Complex: *x = exact_value_to_complex(*x); return; + case ExactValue_Quaternion: + *x = exact_value_to_quaternion(*x); + return; + } + break; + + case ExactValue_Complex: + switch (y->kind) { + case ExactValue_Quaternion: + *x = exact_value_to_quaternion(*x); + return; } break; } @@ -606,6 +728,56 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) break; } + case ExactValue_Quaternion: { + y = exact_value_to_quaternion(y); + f64 xr = x.value_quaternion.real; + f64 xi = x.value_quaternion.imag; + f64 xj = x.value_quaternion.jmag; + f64 xk = x.value_quaternion.kmag; + f64 yr = y.value_quaternion.real; + f64 yi = y.value_quaternion.imag; + f64 yj = y.value_quaternion.jmag; + f64 yk = y.value_quaternion.kmag; + + + f64 real = 0; + f64 imag = 0; + f64 jmag = 0; + f64 kmag = 0; + + switch (op) { + case Token_Add: + real = xr + yr; + imag = xi + yi; + jmag = xj + yj; + kmag = xk + yk; + break; + case Token_Sub: + real = xr - yr; + imag = xi - yi; + jmag = xj - yj; + kmag = xk - yk; + break; + case Token_Mul: + imag = xr * yi + xi * yr + xj * yk - xk * yj; + jmag = xr * yj - xi * yk + xj * yr + xk * yi; + kmag = xr * yk + xi * yj - xj * yi + xk * yr; + real = xr * yr - xi * yi - xj * yj - xk * yk; + break; + case Token_Quo: { + f64 invmag2 = 1.0 / (yr*yr + yi*yi + yj*yj + yk*yk); + imag = (xr * -yi + xi * +yr + xj * -yk - xk * -yj) * invmag2; + jmag = (xr * -yj - xi * -yk + xj * +yr + xk * -yi) * invmag2; + kmag = (xr * -yk + xi * -yj - xj * -yi + xk * +yr) * invmag2; + real = (xr * +yr - xi * -yi - xj * -yj - xk * -yk) * invmag2; + break; + } + default: goto error; + } + return exact_value_quaternion(real, imag, jmag, kmag); + break; + } + case ExactValue_String: { if (op != Token_Add) goto error; diff --git a/src/ir.cpp b/src/ir.cpp index 2499b848f..2a4bb028b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3853,6 +3853,60 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue * return ir_emit_load(proc, res); } + if (is_type_quaternion(t_left)) { + ir_emit_comment(proc, str_lit("complex.arith.begin")); + defer (ir_emit_comment(proc, str_lit("complex.arith.end"))); + + right = ir_emit_conv(proc, right, t_left); + + Type *ft = base_complex_elem_type(t_left); + + if (op == Token_Add || op == Token_Sub) { + irValue *res = ir_add_local_generated(proc, type, false); // NOTE: initialized in full later + irValue *x0 = ir_emit_struct_ev(proc, left, 0); + irValue *x1 = ir_emit_struct_ev(proc, left, 1); + irValue *x2 = ir_emit_struct_ev(proc, left, 2); + irValue *x3 = ir_emit_struct_ev(proc, left, 3); + + irValue *y0 = ir_emit_struct_ev(proc, right, 0); + irValue *y1 = ir_emit_struct_ev(proc, right, 1); + irValue *y2 = ir_emit_struct_ev(proc, right, 2); + irValue *y3 = ir_emit_struct_ev(proc, right, 3); + + irValue *z0 = ir_emit_arith(proc, op, x0, y0, ft); + irValue *z1 = ir_emit_arith(proc, op, x1, y1, ft); + irValue *z2 = ir_emit_arith(proc, op, x2, y2, ft); + irValue *z3 = ir_emit_arith(proc, op, x3, y3, ft); + + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), z0); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), z1); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 2), z2); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 3), z3); + + return ir_emit_load(proc, res); + } else if (op == Token_Mul) { + auto args = array_make(heap_allocator(), 2); + args[0] = left; + args[1] = right; + + switch (8*type_size_of(ft)) { + case 32: return ir_emit_runtime_call(proc, "mul_quaternion128", args); + case 64: return ir_emit_runtime_call(proc, "mul_quaternion256", args); + default: GB_PANIC("Unknown float type"); break; + } + } else if (op == Token_Quo) { + auto args = array_make(heap_allocator(), 2); + args[0] = left; + args[1] = right; + + switch (8*type_size_of(ft)) { + case 32: return ir_emit_runtime_call(proc, "quo_quaternion128", args); + case 64: return ir_emit_runtime_call(proc, "quo_quaternion256", args); + default: GB_PANIC("Unknown float type"); break; + } + } + } + #if 0 if (op == Token_Add) { @@ -4188,7 +4242,7 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal } if (is_type_complex(a)) { - char *runtime_proc = ""; + char const *runtime_proc = ""; i64 sz = 8*type_size_of(a); switch (sz) { case 64: @@ -4212,6 +4266,31 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal return ir_emit_runtime_call(proc, runtime_proc, args); } + if (is_type_quaternion(a)) { + char const *runtime_proc = ""; + i64 sz = 8*type_size_of(a); + switch (sz) { + case 128: + switch (op_kind) { + case Token_CmpEq: runtime_proc = "quaternion128_eq"; break; + case Token_NotEq: runtime_proc = "quaternion128_ne"; break; + } + break; + case 256: + switch (op_kind) { + case Token_CmpEq: runtime_proc = "quaternion256_eq"; break; + case Token_NotEq: runtime_proc = "quaternion256_ne"; break; + } + break; + } + GB_ASSERT(runtime_proc != nullptr); + + auto args = array_make(ir_allocator(), 2); + args[0] = left; + args[1] = right; + return ir_emit_runtime_call(proc, runtime_proc, args); + } + if (is_type_bit_set(a)) { switch (op_kind) { case Token_Lt: @@ -4295,11 +4374,18 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) { case 0: result_type = alloc_type_pointer(ft); break; case 1: result_type = alloc_type_pointer(ft); break; } + } else if (is_type_quaternion(t)) { + Type *ft = base_complex_elem_type(t); + switch (index) { + case 0: result_type = alloc_type_pointer(ft); break; + case 1: result_type = alloc_type_pointer(ft); break; + case 2: result_type = alloc_type_pointer(ft); break; + case 3: result_type = alloc_type_pointer(ft); break; + } } else if (is_type_slice(t)) { switch (index) { case 0: result_type = alloc_type_pointer(alloc_type_pointer(t->Slice.elem)); break; case 1: result_type = alloc_type_pointer(t_int); break; - case 2: result_type = alloc_type_pointer(t_int); break; } } else if (is_type_string(t)) { switch (index) { @@ -4370,6 +4456,17 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) { } break; } + case Basic_quaternion128: case Basic_quaternion256: + { + Type *ft = base_complex_elem_type(t); + switch (index) { + case 0: result_type = ft; break; + case 1: result_type = ft; break; + case 2: result_type = ft; break; + case 3: result_type = ft; break; + } + break; + } } break; case Type_Struct: @@ -4752,6 +4849,8 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { ev = exact_value_to_float(ev); } else if (is_type_complex(dst)) { ev = exact_value_to_complex(ev); + } else if (is_type_quaternion(dst)) { + ev = exact_value_to_quaternion(ev); } else if (is_type_string(dst)) { // Handled elsewhere GB_ASSERT_MSG(ev.kind == ExactValue_String, "%d", ev.kind); @@ -4875,6 +4974,49 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { return ir_emit_load(proc, gen); } + if (is_type_quaternion(src) && is_type_quaternion(dst)) { + // @QuaternionLayout + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, false); + irValue *q0 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 0), ft); + irValue *q1 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 1), ft); + irValue *q2 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 2), ft); + irValue *q3 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 3), ft); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), q0); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 1), q1); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 2), q2); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), q3); + return ir_emit_load(proc, gen); + } + + if (is_type_float(src) && is_type_complex(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, true); + irValue *real = ir_emit_conv(proc, value, ft); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), real); + return ir_emit_load(proc, gen); + } + if (is_type_float(src) && is_type_quaternion(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, true); + irValue *real = ir_emit_conv(proc, value, ft); + // @QuaternionLayout + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), real); + return ir_emit_load(proc, gen); + } + if (is_type_complex(src) && is_type_quaternion(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, true); + irValue *real = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 0), ft); + irValue *imag = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 1), ft); + // @QuaternionLayout + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), imag); + return ir_emit_load(proc, gen); + } + + + // float <-> integer if (is_type_float(src) && is_type_integer(dst)) { irConvKind kind = irConv_fptosi; @@ -5315,6 +5457,7 @@ enum Typeid_Kind : u8 { Typeid_Rune, Typeid_Float, Typeid_Complex, + Typeid_Quaternion, Typeid_String, Typeid_Boolean, Typeid_Any, @@ -6067,17 +6210,77 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu return ir_emit_load(proc, dst); } + case BuiltinProc_quaternion: { + ir_emit_comment(proc, str_lit("quaternion")); + irValue *real = ir_build_expr(proc, ce->args[0]); + irValue *imag = ir_build_expr(proc, ce->args[1]); + irValue *jmag = ir_build_expr(proc, ce->args[2]); + irValue *kmag = ir_build_expr(proc, ce->args[3]); + + // @QuaternionLayout + irValue *dst = ir_add_local_generated(proc, tv.type, false); + Type *ft = base_complex_elem_type(tv.type); + real = ir_emit_conv(proc, real, ft); + imag = ir_emit_conv(proc, imag, ft); + jmag = ir_emit_conv(proc, jmag, ft); + kmag = ir_emit_conv(proc, kmag, ft); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 3), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 0), imag); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 1), jmag); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 2), kmag); + + return ir_emit_load(proc, dst); + } + case BuiltinProc_real: { ir_emit_comment(proc, str_lit("real")); irValue *val = ir_build_expr(proc, ce->args[0]); - irValue *real = ir_emit_struct_ev(proc, val, 0); - return ir_emit_conv(proc, real, tv.type); + if (is_type_complex(ir_type(val))) { + irValue *real = ir_emit_struct_ev(proc, val, 0); + return ir_emit_conv(proc, real, tv.type); + } else if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *real = ir_emit_struct_ev(proc, val, 3); + return ir_emit_conv(proc, real, tv.type); + } + GB_PANIC("invalid type for real"); + return nullptr; } case BuiltinProc_imag: { ir_emit_comment(proc, str_lit("imag")); irValue *val = ir_build_expr(proc, ce->args[0]); - irValue *imag = ir_emit_struct_ev(proc, val, 1); - return ir_emit_conv(proc, imag, tv.type); + if (is_type_complex(ir_type(val))) { + irValue *imag = ir_emit_struct_ev(proc, val, 1); + return ir_emit_conv(proc, imag, tv.type); + } else if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *imag = ir_emit_struct_ev(proc, val, 0); + return ir_emit_conv(proc, imag, tv.type); + } + GB_PANIC("invalid type for imag"); + return nullptr; + } + case BuiltinProc_jmag: { + ir_emit_comment(proc, str_lit("jmag")); + irValue *val = ir_build_expr(proc, ce->args[0]); + if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *imag = ir_emit_struct_ev(proc, val, 1); + return ir_emit_conv(proc, imag, tv.type); + } + GB_PANIC("invalid type for jmag"); + return nullptr; + } + case BuiltinProc_kmag: { + ir_emit_comment(proc, str_lit("kmag")); + irValue *val = ir_build_expr(proc, ce->args[0]); + if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *imag = ir_emit_struct_ev(proc, val, 2); + return ir_emit_conv(proc, imag, tv.type); + } + GB_PANIC("invalid type for kmag"); + return nullptr; } case BuiltinProc_conj: { @@ -6092,6 +6295,20 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu imag = ir_emit_unary_arith(proc, Token_Sub, imag, ir_type(imag)); ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), real); ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), imag); + } else if (is_type_quaternion(t)) { + // @QuaternionLayout + res = ir_add_local_generated(proc, tv.type, false); + irValue *real = ir_emit_struct_ev(proc, val, 3); + irValue *imag = ir_emit_struct_ev(proc, val, 0); + irValue *jmag = ir_emit_struct_ev(proc, val, 1); + irValue *kmag = ir_emit_struct_ev(proc, val, 2); + imag = ir_emit_unary_arith(proc, Token_Sub, imag, ir_type(imag)); + jmag = ir_emit_unary_arith(proc, Token_Sub, jmag, ir_type(jmag)); + kmag = ir_emit_unary_arith(proc, Token_Sub, kmag, ir_type(kmag)); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 3), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), imag); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), jmag); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 2), kmag); } return ir_emit_load(proc, res); } @@ -6162,7 +6379,16 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu return x; } ir_emit_comment(proc, str_lit("abs")); - if (is_type_complex(t)) { + if (is_type_quaternion(t)) { + i64 sz = 8*type_size_of(t); + auto args = array_make(ir_allocator(), 1); + args[0] = x; + switch (sz) { + case 128: return ir_emit_runtime_call(proc, "abs_quaternion128", args); + case 256: return ir_emit_runtime_call(proc, "abs_quaternion256", args); + } + GB_PANIC("Unknown complex type"); + } else if (is_type_complex(t)) { i64 sz = 8*type_size_of(t); auto args = array_make(ir_allocator(), 1); args[0] = x; @@ -9876,6 +10102,11 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info tag = ir_emit_conv(proc, variant_ptr, t_type_info_complex_ptr); break; + case Basic_quaternion128: + case Basic_quaternion256: + tag = ir_emit_conv(proc, variant_ptr, t_type_info_quaternion_ptr); + break; + case Basic_rawptr: tag = ir_emit_conv(proc, variant_ptr, t_type_info_pointer_ptr); break; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index c1e41c4b0..30ad914a9 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -418,20 +418,23 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) { } return; - // case Basic_f16: ir_write_str_lit(f, "half"); return; - case Basic_f32: ir_write_str_lit(f, "float"); return; - case Basic_f64: ir_write_str_lit(f, "double"); return; + // case Basic_f16: ir_write_str_lit(f, "half"); return; + case Basic_f32: ir_write_str_lit(f, "float"); return; + case Basic_f64: ir_write_str_lit(f, "double"); return; - // case Basic_complex32: ir_write_str_lit(f, "%%..complex32"); return; - case Basic_complex64: ir_write_str_lit(f, "%..complex64"); return; - case Basic_complex128: ir_write_str_lit(f, "%..complex128"); return; + // case Basic_complex32: ir_write_str_lit(f, "%%..complex32"); return; + case Basic_complex64: ir_write_str_lit(f, "%..complex64"); return; + case Basic_complex128: ir_write_str_lit(f, "%..complex128"); return; - case Basic_any: ir_write_str_lit(f, "%..any"); return; - case Basic_rawptr: ir_write_str_lit(f, "%..rawptr"); return; - case Basic_string: ir_write_str_lit(f, "%..string"); return; - case Basic_cstring: ir_write_str_lit(f, "i8*"); return; + case Basic_quaternion128: ir_write_str_lit(f, "%..quaternion128"); return; + case Basic_quaternion256: ir_write_str_lit(f, "%..quaternion256"); return; - case Basic_typeid: ir_write_str_lit(f, "%..typeid"); return; + case Basic_any: ir_write_str_lit(f, "%..any"); return; + case Basic_rawptr: ir_write_str_lit(f, "%..rawptr"); return; + case Basic_string: ir_write_str_lit(f, "%..string"); return; + case Basic_cstring: ir_write_str_lit(f, "i8*"); return; + + case Basic_typeid: ir_write_str_lit(f, "%..typeid"); return; } break; @@ -811,6 +814,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * break; } case ExactValue_Complex: { + // xy/ri format type = core_type(type); GB_ASSERT_MSG(is_type_complex(type), "%s", type_to_string(type)); Type *ft = base_complex_elem_type(type); @@ -823,6 +827,26 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_byte(f, '}'); break; } + + case ExactValue_Quaternion: { + // xyzw/ijkr format + type = core_type(type); + GB_ASSERT_MSG(is_type_quaternion(type), "%s", type_to_string(type)); + Type *ft = base_complex_elem_type(type); + ir_write_byte(f, ' '); + ir_write_byte(f, '{'); + ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.imag), ft); + ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.jmag), ft); + ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.kmag), ft); + ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.real), ft); + ir_write_byte(f, '}'); + break; + } + case ExactValue_Pointer: if (value.value_pointer == 0) { if (is_type_typeid(type)) { @@ -2246,6 +2270,13 @@ void print_llvm_ir(irGen *ir) { ir_print_encoded_local(f, str_lit("..complex128")); ir_write_str_lit(f, " = type {double, double} ; Basic_complex128\n"); + ir_print_encoded_local(f, str_lit("..quaternion64")); + ir_write_str_lit(f, " = type {half, half, half, half} ; Basic_quaternion64\n"); + ir_print_encoded_local(f, str_lit("..quaternion128")); + ir_write_str_lit(f, " = type {float, float, float, float} ; Basic_quaternion128\n"); + ir_print_encoded_local(f, str_lit("..quaternion256")); + ir_write_str_lit(f, " = type {double, double, double, double} ; Basic_quaternion256\n"); + ir_print_encoded_local(f, str_lit("..typeid")); ir_write_str_lit(f, " = type "); ir_print_type(f, m, t_uintptr); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index a551f0545..5ba402858 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -745,7 +745,10 @@ exponent: scan_mantissa(t, 10); } - if (t->curr_rune == 'i') { + switch (t->curr_rune) { + case 'i': + case 'j': + case 'k': token.kind = Token_Imag; advance_to_next_rune(t); } diff --git a/src/types.cpp b/src/types.cpp index b5c9152b6..94b89d8f5 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -32,6 +32,9 @@ enum BasicKind { Basic_complex64, Basic_complex128, + Basic_quaternion128, + Basic_quaternion256, + Basic_int, Basic_uint, Basic_uintptr, @@ -66,6 +69,7 @@ enum BasicKind { Basic_UntypedInteger, Basic_UntypedFloat, Basic_UntypedComplex, + Basic_UntypedQuaternion, Basic_UntypedString, Basic_UntypedRune, Basic_UntypedNil, @@ -82,17 +86,18 @@ enum BasicFlag { BasicFlag_Unsigned = GB_BIT(2), BasicFlag_Float = GB_BIT(3), BasicFlag_Complex = GB_BIT(4), - BasicFlag_Pointer = GB_BIT(5), - BasicFlag_String = GB_BIT(6), - BasicFlag_Rune = GB_BIT(7), - BasicFlag_Untyped = GB_BIT(8), + BasicFlag_Quaternion = GB_BIT(5), + BasicFlag_Pointer = GB_BIT(6), + BasicFlag_String = GB_BIT(7), + BasicFlag_Rune = GB_BIT(8), + BasicFlag_Untyped = GB_BIT(9), - BasicFlag_LLVM = GB_BIT(10), + BasicFlag_LLVM = GB_BIT(11), BasicFlag_EndianLittle = GB_BIT(13), BasicFlag_EndianBig = GB_BIT(14), - BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex, + BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex | BasicFlag_Quaternion, BasicFlag_Ordered = BasicFlag_Integer | BasicFlag_Float | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune, BasicFlag_OrderedNumeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Rune, BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune, @@ -342,6 +347,9 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_complex64, BasicFlag_Complex, 8, STR_LIT("complex64")}}, {Type_Basic, {Basic_complex128, BasicFlag_Complex, 16, STR_LIT("complex128")}}, + {Type_Basic, {Basic_quaternion128, BasicFlag_Quaternion, 16, STR_LIT("quaternion128")}}, + {Type_Basic, {Basic_quaternion256, BasicFlag_Quaternion, 32, STR_LIT("quaternion256")}}, + {Type_Basic, {Basic_int, BasicFlag_Integer, -1, STR_LIT("int")}}, {Type_Basic, {Basic_uint, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uint")}}, {Type_Basic, {Basic_uintptr, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uintptr")}}, @@ -377,6 +385,7 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}}, {Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, 0, STR_LIT("untyped float")}}, {Type_Basic, {Basic_UntypedComplex, BasicFlag_Complex | BasicFlag_Untyped, 0, STR_LIT("untyped complex")}}, + {Type_Basic, {Basic_UntypedQuaternion, BasicFlag_Quaternion | BasicFlag_Untyped, 0, STR_LIT("untyped quaternion")}}, {Type_Basic, {Basic_UntypedString, BasicFlag_String | BasicFlag_Untyped, 0, STR_LIT("untyped string")}}, {Type_Basic, {Basic_UntypedRune, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped rune")}}, {Type_Basic, {Basic_UntypedNil, BasicFlag_Untyped, 0, STR_LIT("untyped nil")}}, @@ -412,6 +421,9 @@ gb_global Type *t_f64 = &basic_types[Basic_f64]; gb_global Type *t_complex64 = &basic_types[Basic_complex64]; gb_global Type *t_complex128 = &basic_types[Basic_complex128]; +gb_global Type *t_quaternion128 = &basic_types[Basic_quaternion128]; +gb_global Type *t_quaternion256 = &basic_types[Basic_quaternion256]; + gb_global Type *t_int = &basic_types[Basic_int]; gb_global Type *t_uint = &basic_types[Basic_uint]; gb_global Type *t_uintptr = &basic_types[Basic_uintptr]; @@ -446,6 +458,7 @@ gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool]; gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger]; gb_global Type *t_untyped_float = &basic_types[Basic_UntypedFloat]; gb_global Type *t_untyped_complex = &basic_types[Basic_UntypedComplex]; +gb_global Type *t_untyped_quaternion = &basic_types[Basic_UntypedQuaternion]; gb_global Type *t_untyped_string = &basic_types[Basic_UntypedString]; gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune]; gb_global Type *t_untyped_nil = &basic_types[Basic_UntypedNil]; @@ -472,6 +485,7 @@ gb_global Type *t_type_info_integer = nullptr; gb_global Type *t_type_info_rune = nullptr; gb_global Type *t_type_info_float = nullptr; gb_global Type *t_type_info_complex = nullptr; +gb_global Type *t_type_info_quaternion = nullptr; gb_global Type *t_type_info_any = nullptr; gb_global Type *t_type_info_typeid = nullptr; gb_global Type *t_type_info_string = nullptr; @@ -496,6 +510,7 @@ gb_global Type *t_type_info_integer_ptr = nullptr; gb_global Type *t_type_info_rune_ptr = nullptr; gb_global Type *t_type_info_float_ptr = nullptr; gb_global Type *t_type_info_complex_ptr = nullptr; +gb_global Type *t_type_info_quaternion_ptr = nullptr; gb_global Type *t_type_info_any_ptr = nullptr; gb_global Type *t_type_info_typeid_ptr = nullptr; gb_global Type *t_type_info_string_ptr = nullptr; @@ -924,6 +939,13 @@ bool is_type_complex(Type *t) { } return false; } +bool is_type_quaternion(Type *t) { + t = core_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Quaternion) != 0; + } + return false; +} bool is_type_f32(Type *t) { t = core_type(t); if (t->kind == Type_Basic) { @@ -1063,12 +1085,15 @@ bool is_type_simple_compare(Type *t) { Type *base_complex_elem_type(Type *t) { t = core_type(t); - if (is_type_complex(t)) { + if (t->kind == Type_Basic) { switch (t->Basic.kind) { - // case Basic_complex32: return t_f16; - case Basic_complex64: return t_f32; - case Basic_complex128: return t_f64; - case Basic_UntypedComplex: return t_untyped_float; + // case Basic_complex32: return t_f16; + case Basic_complex64: return t_f32; + case Basic_complex128: return t_f64; + case Basic_quaternion128: return t_f32; + case Basic_quaternion256: return t_f64; + case Basic_UntypedComplex: return t_untyped_float; + case Basic_UntypedQuaternion: return t_untyped_float; } } GB_PANIC("Invalid complex type"); @@ -1818,6 +1843,7 @@ Type *default_type(Type *type) { case Basic_UntypedInteger: return t_int; case Basic_UntypedFloat: return t_f64; case Basic_UntypedComplex: return t_complex128; + case Basic_UntypedQuaternion: return t_quaternion256; case Basic_UntypedString: return t_string; case Basic_UntypedRune: return t_rune; } @@ -2358,6 +2384,8 @@ i64 type_align_of_internal(Type *t, TypePath *path) { case Basic_complex64: case Basic_complex128: return type_size_of_internal(t, path) / 2; + case Basic_quaternion128: case Basic_quaternion256: + return type_size_of_internal(t, path) / 4; } } break; -- cgit v1.2.3 From d118fc569abed3338cf85c3ec2a0296340d9c04f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 29 Aug 2019 16:45:36 +0100 Subject: Add intrinsincs.type_is_quaternion --- src/check_expr.cpp | 2 ++ src/checker_builtin_procs.hpp | 2 ++ 2 files changed, 4 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6a9a53275..7c4c4e6e4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3214,6 +3214,7 @@ BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_end - BuiltinProc__ty is_type_rune, is_type_float, is_type_complex, + is_type_quaternion, is_type_string, is_type_typeid, is_type_any, @@ -4776,6 +4777,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_type_is_rune: case BuiltinProc_type_is_float: case BuiltinProc_type_is_complex: + case BuiltinProc_type_is_quaternion: case BuiltinProc_type_is_string: case BuiltinProc_type_is_typeid: case BuiltinProc_type_is_any: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 0135b3c50..b119452d4 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -120,6 +120,7 @@ BuiltinProc__type_begin, BuiltinProc_type_is_rune, BuiltinProc_type_is_float, BuiltinProc_type_is_complex, + BuiltinProc_type_is_quaternion, BuiltinProc_type_is_string, BuiltinProc_type_is_typeid, BuiltinProc_type_is_any, @@ -278,6 +279,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_rune"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_float"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_complex"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_quaternion"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_string"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_typeid"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, -- cgit v1.2.3 From 07ced1cf0eed9c30813d49dad0c33f29f0c161ef Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 31 Aug 2019 11:12:41 +0100 Subject: Fix variable dependency ordering issues caused by procedure literals --- src/check_decl.cpp | 23 +++++++++++++++-------- src/check_expr.cpp | 3 ++- src/checker.cpp | 9 +++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index d87e6def0..b5b9dc61d 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1190,17 +1190,24 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty check_scope_usage(ctx->checker, ctx->scope); +#if 0 if (decl->parent != nullptr) { - // NOTE(bill): Add the dependencies from the procedure literal (lambda) - for_array(i, decl->deps.entries) { - Entity *e = decl->deps.entries[i].ptr; - ptr_set_add(&decl->parent->deps, e); - } - for_array(i, decl->type_info_deps.entries) { - Type *t = decl->type_info_deps.entries[i].ptr; - ptr_set_add(&decl->parent->type_info_deps, t); + Scope *ps = decl->parent->scope; + if (ps->flags & (ScopeFlag_File & ScopeFlag_Pkg & ScopeFlag_Global)) { + return; + } else { + // NOTE(bill): Add the dependencies from the procedure literal (lambda) + for_array(i, decl->deps.entries) { + Entity *e = decl->deps.entries[i].ptr; + ptr_set_add(&decl->parent->deps, e); + } + for_array(i, decl->type_info_deps.entries) { + Type *t = decl->type_info_deps.entries[i].ptr; + ptr_set_add(&decl->parent->type_info_deps, t); + } } } +#endif } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7c4c4e6e4..86cba0f94 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4864,7 +4864,7 @@ isize add_dependencies_from_unpacking(CheckerContext *c, Entity **lhs, isize lhs c->decl = decl; // will be reset by the 'defer' any way for_array(k, decl->deps.entries) { Entity *dep = decl->deps.entries[k].ptr; - add_declaration_dependency(c, dep); // TODO(bill): Should this be here? + add_declaration_dependency(c, dep); // TODO(bill): Should this be here? } } } @@ -6519,6 +6519,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type decl = make_decl_info(ctx.allocator, ctx.scope, ctx.decl); decl->proc_lit = node; ctx.decl = decl; + defer (ctx.decl = ctx.decl->parent); if (pl->tags != 0) { error(node, "A procedure literal cannot have tags"); diff --git a/src/checker.cpp b/src/checker.cpp index fc8cb5c63..9fa6d4555 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1445,6 +1445,14 @@ void add_min_dep_type_info(Checker *c, Type *t) { add_min_dep_type_info(c, t_type_info_float); add_min_dep_type_info(c, t_f64); break; + case Basic_quaternion128: + add_min_dep_type_info(c, t_type_info_float); + add_min_dep_type_info(c, t_f32); + break; + case Basic_quaternion256: + add_min_dep_type_info(c, t_type_info_float); + add_min_dep_type_info(c, t_f64); + break; } break; @@ -1779,6 +1787,7 @@ Array generate_entity_dependency_graph(CheckerInfo *info) { EntityGraphNode *n = G[i]; n->index = i; n->dep_count = n->succ.entries.count; + GB_ASSERT(n->dep_count >= 0); } -- cgit v1.2.3 From b9d3129fb3a4ba7ef49cea69d086a7f705819f2e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 31 Aug 2019 20:13:28 +0100 Subject: `where` clauses for procedure literals --- core/odin/ast/ast.odin | 2 + core/odin/parser/parser.odin | 18 +++++++ core/odin/token/token.odin | 2 + examples/demo/demo.odin | 65 ++++++++++++++++++++++++ src/check_decl.cpp | 33 ++++++++++--- src/check_expr.cpp | 115 +++++++++++++++++++++++++++++++++++++++++-- src/checker.cpp | 8 +++ src/parser.cpp | 50 ++++++++++++++----- src/parser.hpp | 2 + src/tokenizer.cpp | 1 + 10 files changed, 272 insertions(+), 24 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 87893e0e4..f1ea79584 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -99,6 +99,8 @@ Proc_Lit :: struct { body: ^Stmt, tags: Proc_Tags, inlining: Proc_Inlining, + where_token: token.Token, + where_clauses: []^Expr, } Comp_Lit :: struct { diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 1f5add69a..a0d4d639e 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1985,13 +1985,29 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { type := parse_proc_type(p, tok); + where_token: token.Token; + where_clauses: []^ast.Expr; + if (p.curr_tok.kind == token.Where) { + where_token = expect_token(p, token.Where); + prev_level := p.expr_level; + p.expr_level = -1; + where_clauses = parse_rhs_expr_list(p); + p.expr_level = prev_level; + } + if p.allow_type && p.expr_level < 0 { + if where_token.kind != token.Invalid { + error(p, where_token.pos, "'where' clauses are not allowed on procedure types"); + } return type; } body: ^ast.Stmt; if allow_token(p, token.Undef) { // Okay + if where_token.kind != token.Invalid { + error(p, where_token.pos, "'where' clauses are not allowed on procedure literals without a defined body (replaced with ---"); + } } else if p.curr_tok.kind == token.Open_Brace { prev_proc := p.curr_proc; p.curr_proc = type; @@ -2009,6 +2025,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { pl := ast.new(ast.Proc_Lit, tok.pos, end_pos(p.prev_tok)); pl.type = type; pl.body = body; + pl.where_token = where_token; + pl.where_clauses = where_clauses; return pl; case token.Dollar: diff --git a/core/odin/token/token.odin b/core/odin/token/token.odin index d41fa6d05..737ff3586 100644 --- a/core/odin/token/token.odin +++ b/core/odin/token/token.odin @@ -118,6 +118,7 @@ using Kind :: enum u32 { Package, Typeid, When, + Where, If, Else, For, @@ -252,6 +253,7 @@ tokens := [Kind.COUNT]string { "package", "typeid", "when", + "where", "if", "else", "for", diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index fe390e5b0..92d1c17bd 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -4,6 +4,7 @@ import "core:fmt" import "core:mem" import "core:os" import "core:reflect" +import "intrinsics" when os.OS == "windows" { import "core:thread" @@ -1094,6 +1095,69 @@ inline_for_statement :: proc() { } } +procedure_where_clauses :: proc() { + fmt.println("\n#procedure 'where' clauses"); + + { // Sanity checks + simple_sanity_check :: proc(x: [2]int) + where len(x) > 1, + type_of(x) == [2]int { + fmt.println(x); + } + } + { // Parametric polymorphism checks + cross_2d :: proc(a, b: $T/[2]$E) -> E + where intrinsics.type_is_numeric(E) { + return a.x*b.y - a.y*b.x; + } + cross_3d :: proc(a, b: $T/[3]$E) -> T + where intrinsics.type_is_numeric(E) { + x := a.y*b.z - a.z*b.y; + y := a.z*b.x - a.x*b.z; + z := a.x*b.y - a.y*b.z; + return T{x, y, z}; + } + + a := [2]int{1, 2}; + b := [2]int{5, -3}; + fmt.println(cross_2d(a, b)); + + x := [3]f32{1, 4, 9}; + y := [3]f32{-5, 0, 3}; + fmt.println(cross_3d(x, y)); + + // Failure case + // i := [2]bool{true, false}; + // j := [2]bool{false, true}; + // fmt.println(cross_2d(i, j)); + + } + + { // Procedure groups usage + foo :: proc(x: [$N]int) -> bool + where N > 2 { + fmt.println(#procedure, "was called with the parameter", x); + return true; + } + + bar :: proc(x: [$N]int) -> bool + where 0 < N, + N <= 2 { + fmt.println(#procedure, "was called with the parameter", x); + return false; + } + + baz :: proc{foo, bar}; + + x := [3]int{1, 2, 3}; + y := [2]int{4, 9}; + ok_x := baz(x); + ok_y := baz(y); + assert(ok_x == true); + assert(ok_y == false); + } +} + main :: proc() { when true { general_stuff(); @@ -1115,5 +1179,6 @@ main :: proc() { reflection(); quaternions(); inline_for_statement(); + procedure_where_clauses(); } } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 7e019d82b..156c874ce 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -936,7 +936,6 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) ptr_set_destroy(&entity_set); - for_array(j, pge->entities) { Entity *p = pge->entities[j]; if (p->type == t_invalid) { @@ -962,27 +961,40 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) defer (end_error_block()); ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type); - switch (kind) { + bool both_have_where_clauses = false; + if (p->decl_info->proc_lit != nullptr && q->decl_info->proc_lit != nullptr) { + GB_ASSERT(p->decl_info->proc_lit->kind == Ast_ProcLit); + GB_ASSERT(q->decl_info->proc_lit->kind == Ast_ProcLit); + auto pl = &p->decl_info->proc_lit->ProcLit; + auto ql = &q->decl_info->proc_lit->ProcLit; + + // Allow collisions if the procedures both have 'where' clauses and are both polymorphic + bool pw = pl->where_token.kind != Token_Invalid && is_type_polymorphic(p->type, true); + bool qw = ql->where_token.kind != Token_Invalid && is_type_polymorphic(q->type, true); + both_have_where_clauses = pw && qw; + } + + if (!both_have_where_clauses) switch (kind) { case ProcOverload_Identical: - error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name)); + error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; // case ProcOverload_CallingConvention: - // error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name)); + // error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); // is_invalid = true; // break; case ProcOverload_ParamVariadic: - error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name)); + error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; case ProcOverload_ResultCount: case ProcOverload_ResultTypes: - error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in this scope", LIT(name)); + error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; case ProcOverload_Polymorphic: #if 0 - error(p->token, "Overloaded procedure '%.*s' has a polymorphic counterpart in this scope which is not allowed", LIT(name)); + error(p->token, "Overloaded procedure '%.*s' has a polymorphic counterpart in the procedure group '%.*s' which is not allowed", LIT(name), LIT(proc_group_name)); is_invalid = true; #endif break; @@ -1163,6 +1175,13 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty } } + + bool where_clause_ok = evaluate_where_clauses(ctx, decl, true); + if (!where_clause_ok) { + // NOTE(bill, 2019-08-31): Don't check the body as the where clauses failed + return; + } + check_open_scope(ctx, body); { for_array(i, using_entities) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 86cba0f94..4c8cd61a7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5470,6 +5470,74 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize return lhs; } + +bool evaluate_where_clauses(CheckerContext *ctx, DeclInfo *decl, bool print_err) { + Ast *proc_lit = decl->proc_lit; + GB_ASSERT(proc_lit != nullptr); + GB_ASSERT(proc_lit->kind == Ast_ProcLit); + + if (proc_lit->ProcLit.where_token.kind != Token_Invalid) { + auto &clauses = proc_lit->ProcLit.where_clauses; + for_array(i, clauses) { + Ast *clause = clauses[i]; + Operand o = {}; + check_expr(ctx, &o, clause); + if (o.mode != Addressing_Constant) { + if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation"); + return false; + } else if (o.value.kind != ExactValue_Bool) { + if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation"); + return false; + } else if (!o.value.value_bool) { + if (print_err) { + gbString str = expr_to_string(clause); + error(clause, "'where' clause evaluated to false:\n\t%s", str); + gb_string_free(str); + + if (decl->scope != nullptr) { + isize print_count = 0; + for_array(j, decl->scope->elements.entries) { + Entity *e = decl->scope->elements.entries[j].value; + switch (e->kind) { + case Entity_TypeName: { + if (print_count == 0) error_line("\n\tWith the following definitions:\n"); + + gbString str = type_to_string(e->type); + error_line("\t\t%.*s :: %s;\n", LIT(e->token.string), str); + gb_string_free(str); + print_count += 1; + break; + } + case Entity_Constant: { + if (print_count == 0) error_line("\n\tWith the following definitions:\n"); + + gbString str = exact_value_to_string(e->Constant.value); + if (is_type_untyped(e->type)) { + error_line("\t\t%.*s :: %s;\n", LIT(e->token.string), str); + } else { + gbString t = type_to_string(e->type); + error_line("\t\t%.*s : %s : %s;\n", LIT(e->token.string), t, str); + gb_string_free(t); + } + gb_string_free(str); + + print_count += 1; + break; + } + } + } + } + + } + return false; + } + } + } + + return true; +} + + CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call) { ast_node(ce, CallExpr, call); @@ -5710,11 +5778,26 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data); - if (err == CallArgumentError_None) { - valids[valid_count].index = i; - valids[valid_count].score = data.score; - valid_count++; + if (err != CallArgumentError_None) { + continue; } + if (data.gen_entity != nullptr) { + Entity *e = data.gen_entity; + DeclInfo *decl = data.gen_entity->decl_info; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + if (!evaluate_where_clauses(&ctx, decl, false)) { + continue; + } + } + + valids[valid_count].index = i; + valids[valid_count].score = data.score; + valid_count++; } } @@ -5822,7 +5905,29 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type if (proc->kind == Entity_Variable) { sep = ":="; } - error_line("\t%.*s %s %s at %.*s(%td:%td)\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column); + error_line("\t%.*s %s %s ", LIT(name), sep, pt); + if (proc->decl_info->proc_lit != nullptr) { + GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit); + auto *pl = &proc->decl_info->proc_lit->ProcLit; + if (pl->where_token.kind != Token_Invalid) { + error_line("\n\t\twhere "); + for_array(j, pl->where_clauses) { + Ast *clause = pl->where_clauses[j]; + if (j != 0) { + error_line("\t\t "); + } + gbString str = expr_to_string(clause); + error_line("%s", str); + gb_string_free(str); + + if (j != pl->where_clauses.count-1) { + error_line(","); + } + } + error_line("\n\t"); + } + } + error_line("at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column); // error_line("\t%.*s %s %s at %.*s(%td:%td) %lld\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column, valids[i].score); } result_type = t_invalid; diff --git a/src/checker.cpp b/src/checker.cpp index b00b4bbac..8fe71b63c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3697,6 +3697,14 @@ void check_proc_info(Checker *c, ProcInfo pi) { return; } + if (pt->is_polymorphic && pt->is_poly_specialized) { + Entity *e = pi.decl->entity; + if ((e->flags & EntityFlag_Used) == 0) { + // NOTE(bill, 2019-08-31): It was never used, don't check + return; + } + } + bool bounds_check = (pi.tags & ProcTag_bounds_check) != 0; bool no_bounds_check = (pi.tags & ProcTag_no_bounds_check) != 0; diff --git a/src/parser.cpp b/src/parser.cpp index e92489020..8490b0e00 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -144,6 +144,7 @@ Ast *clone_ast(Ast *node) { case Ast_ProcLit: n->ProcLit.type = clone_ast(n->ProcLit.type); n->ProcLit.body = clone_ast(n->ProcLit.body); + n->ProcLit.where_clauses = clone_ast_array(n->ProcLit.where_clauses); break; case Ast_CompoundLit: n->CompoundLit.type = clone_ast(n->CompoundLit.type); @@ -612,11 +613,13 @@ Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_ProcLit); result->ProcLit.type = type; result->ProcLit.body = body; result->ProcLit.tags = tags; + result->ProcLit.where_token = where_token; + result->ProcLit.where_clauses = where_clauses; return result; } @@ -1827,15 +1830,41 @@ Ast *parse_operand(AstFile *f, bool lhs) { } Ast *type = parse_proc_type(f, token); + Token where_token = {}; + Array where_clauses = {}; + u64 tags = 0; + + if (f->curr_token.kind == Token_where) { + where_token = expect_token(f, Token_where); + isize prev_level = f->expr_level; + f->expr_level = -1; + where_clauses = parse_rhs_expr_list(f); + f->expr_level = prev_level; + } + + parse_proc_tags(f, &tags); + if ((tags & ProcTag_require_results) != 0) { + syntax_error(f->curr_token, "#require_results has now been replaced as an attribute @(require_results) on the declaration"); + tags &= ~ProcTag_require_results; + } + GB_ASSERT(type->kind == Ast_ProcType); + type->ProcType.tags = tags; if (f->allow_type && f->expr_level < 0) { + if (tags != 0) { + syntax_error(token, "A procedure type cannot have suffix tags"); + } + if (where_token.kind != Token_Invalid) { + syntax_error(where_token, "'where' clauses are not allowed on procedure types"); + } return type; } - u64 tags = type->ProcType.tags; - if (allow_token(f, Token_Undef)) { - return ast_proc_lit(f, type, nullptr, tags); + if (where_token.kind != Token_Invalid) { + syntax_error(where_token, "'where' clauses are not allowed on procedure literals without a defined body (replaced with ---)"); + } + return ast_proc_lit(f, type, nullptr, tags, where_token, where_clauses); } else if (f->curr_token.kind == Token_OpenBrace) { Ast *curr_proc = f->curr_proc; Ast *body = nullptr; @@ -1843,7 +1872,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { body = parse_body(f); f->curr_proc = curr_proc; - return ast_proc_lit(f, type, body, tags); + return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } else if (allow_token(f, Token_do)) { Ast *curr_proc = f->curr_proc; Ast *body = nullptr; @@ -1851,12 +1880,15 @@ Ast *parse_operand(AstFile *f, bool lhs) { body = convert_stmt_to_body(f, parse_stmt(f)); f->curr_proc = curr_proc; - return ast_proc_lit(f, type, body, tags); + return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } if (tags != 0) { syntax_error(token, "A procedure type cannot have suffix tags"); } + if (where_token.kind != Token_Invalid) { + syntax_error(where_token, "'where' clauses are not allowed on procedure types"); + } return type; } @@ -2827,12 +2859,6 @@ Ast *parse_proc_type(AstFile *f, Token proc_token) { results = parse_results(f, &diverging); u64 tags = 0; - parse_proc_tags(f, &tags); - if ((tags & ProcTag_require_results) != 0) { - syntax_error(f->curr_token, "#require_results has now been replaced as an attribute @(require_results) on the declaration"); - tags &= ~ProcTag_require_results; - } - bool is_generic = false; for_array(i, params->FieldList.list) { diff --git a/src/parser.hpp b/src/parser.hpp index 32398592e..26536fe56 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -229,6 +229,8 @@ enum StmtAllowFlag { Ast *body; \ u64 tags; \ ProcInlining inlining; \ + Token where_token; \ + Array where_clauses; \ }) \ AST_KIND(CompoundLit, "compound literal", struct { \ Ast *type; \ diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 618de54b9..d5e04aa1e 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -86,6 +86,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_package, "package"), \ TOKEN_KIND(Token_typeid, "typeid"), \ TOKEN_KIND(Token_when, "when"), \ + TOKEN_KIND(Token_where, "where"), \ TOKEN_KIND(Token_if, "if"), \ TOKEN_KIND(Token_else, "else"), \ TOKEN_KIND(Token_for, "for"), \ -- cgit v1.2.3 From 657103c4cff8f63cfa617d8c4371fd29df7b41a2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 1 Sep 2019 20:02:39 +0100 Subject: ThreadPool for the parser --- core/log/file_console_logger.odin | 32 ++--- core/sort/sort.odin | 7 +- src/check_expr.cpp | 2 +- src/common.cpp | 5 +- src/gb/gb.h | 2 +- src/parser.cpp | 243 ++++++++------------------------------ src/parser.hpp | 13 ++ 7 files changed, 86 insertions(+), 218 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin index 3778d3e53..6bddf7ffb 100644 --- a/core/log/file_console_logger.odin +++ b/core/log/file_console_logger.odin @@ -14,18 +14,18 @@ Level_Headers := []string{ }; Default_Console_Logger_Opts :: Options{ - Option.Level, - Option.Terminal_Color, - Option.Short_File_Path, - Option.Line, - Option.Procedure, + .Level, + .Terminal_Color, + .Short_File_Path, + .Line, + .Procedure, } | Full_Timestamp_Opts; Default_File_Logger_Opts :: Options{ - Option.Level, - Option.Short_File_Path, - Option.Line, - Option.Procedure, + .Level, + .Short_File_Path, + .Line, + .Procedure, } | Full_Timestamp_Opts; @@ -109,10 +109,10 @@ do_level_header :: proc(opts : Options, level : Level, str : ^strings.Builder) { case Level.Error, Level.Fatal : col = RED; } - if Option.Level in opts { - if Option.Terminal_Color in opts do fmt.sbprint(str, col); + if .Level in opts { + if .Terminal_Color in opts do fmt.sbprint(str, col); fmt.sbprint(str, Level_Headers[level]); - if Option.Terminal_Color in opts do fmt.sbprint(str, RESET); + if .Terminal_Color in opts do fmt.sbprint(str, RESET); } } @@ -120,7 +120,7 @@ do_location_header :: proc(opts : Options, buf : ^strings.Builder, location := # if Location_Header_Opts & opts != nil do fmt.sbprint(buf, "["); else do return; file := location.file_path; - if Option.Short_File_Path in opts { + if .Short_File_Path in opts { when os.OS == "windows" do delimiter := '\\'; else do delimiter := '/'; last := 0; for r, i in location.file_path do if r == delimiter do last = i+1; @@ -129,13 +129,13 @@ do_location_header :: proc(opts : Options, buf : ^strings.Builder, location := # if Location_File_Opts & opts != nil do fmt.sbprint(buf, file); - if Option.Procedure in opts { + if .Procedure in opts { if Location_File_Opts & opts != nil do fmt.sbprint(buf, "."); fmt.sbprintf(buf, "%s()", location.procedure); } - if Option.Line in opts { - if Location_File_Opts & opts != nil || Option.Procedure in opts do fmt.sbprint(buf, ":"); + if .Line in opts { + if Location_File_Opts & opts != nil || .Procedure in opts do fmt.sbprint(buf, ":"); fmt.sbprint(buf, location.line); } diff --git a/core/sort/sort.odin b/core/sort/sort.odin index 3c8366def..86953d7dd 100644 --- a/core/sort/sort.odin +++ b/core/sort/sort.odin @@ -1,6 +1,7 @@ package sort import "core:mem" +import "intrinsics" bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { assert(f != nil); @@ -26,7 +27,7 @@ bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { } } -bubble_sort :: proc(array: $A/[]$T) { +bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { count := len(array); init_j, last_j := 0, count-1; @@ -73,7 +74,7 @@ quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { quick_sort_proc(a[i:n], f); } -quick_sort :: proc(array: $A/[]$T) { +quick_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { a := array; n := len(a); if n < 2 do return; @@ -146,7 +147,7 @@ merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { if M & 1 == 0 do copy(arr2, arr1); } -merge_sort :: proc(array: $A/[]$T) { +merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { merge_slices :: proc(arr1, arr2, out: A) { N1, N2 := len(arr1), len(arr2); i, j := 0, 0; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4c8cd61a7..ca1aed970 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -159,7 +159,7 @@ isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0 src = base_type(src); if (!is_type_struct(src)) { - return false; + return 0; } for_array(i, src->Struct.fields) { diff --git a/src/common.cpp b/src/common.cpp index 8085e895c..db6505a36 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -10,13 +10,11 @@ #define GB_IMPLEMENTATION #include "gb/gb.h" - #include #include #include - template gb_inline U bit_cast(V &v) { return reinterpret_cast(v); } @@ -331,7 +329,7 @@ void mul_overflow_u64(u64 x, u64 y, u64 *lo, u64 *hi) { #include "ptr_set.cpp" #include "string_set.cpp" #include "priority_queue.cpp" - +#include "thread_pool.cpp" gb_global String global_module_path = {0}; @@ -873,7 +871,6 @@ ReadDirectoryError read_directory(String path, Array *fi) { info.size = size; info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; array_add(fi, info); - } while (FindNextFileW(find_file, &file_data)); if (fi->count == 0) { diff --git a/src/gb/gb.h b/src/gb/gb.h index adeb554b2..5e74ff2d8 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -4591,7 +4591,7 @@ gb_inline void gb_semaphore_release(gbSemaphore *s) { gb_semaphore_post(s, 1); } gb_inline void gb_semaphore_init (gbSemaphore *s) { s->win32_handle = CreateSemaphoreA(NULL, 0, I32_MAX, NULL); } gb_inline void gb_semaphore_destroy(gbSemaphore *s) { CloseHandle(s->win32_handle); } gb_inline void gb_semaphore_post (gbSemaphore *s, i32 count) { ReleaseSemaphore(s->win32_handle, count, NULL); } - gb_inline void gb_semaphore_wait (gbSemaphore *s) { WaitForSingleObject(s->win32_handle, INFINITE); } + gb_inline void gb_semaphore_wait (gbSemaphore *s) { WaitForSingleObjectEx(s->win32_handle, INFINITE, FALSE); } #elif defined(GB_SYSTEM_OSX) gb_inline void gb_semaphore_init (gbSemaphore *s) { semaphore_create(mach_task_self(), &s->osx_handle, SYNC_POLICY_FIFO, 0); } diff --git a/src/parser.cpp b/src/parser.cpp index 8490b0e00..a210eb661 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4207,16 +4207,30 @@ void parser_add_package(Parser *p, AstPackage *pkg) { error(f->package_token, "Non-unique package name '%.*s'", LIT(pkg->name)); GB_ASSERT((*found)->files.count > 0); TokenPos pos = (*found)->files[0]->package_token.pos; - gb_printf_err("\tpreviously declared at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column); + error_line("\tpreviously declared at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column); } else { map_set(&p->package_map, key, pkg); } } } +ParseFileError process_imported_file(Parser *p, ImportedFile const &imported_file); + +WORKER_TASK_PROC(parser_worker_proc) { + ParserWorkerData *wd = cast(ParserWorkerData *)data; + ParseFileError err = process_imported_file(wd->parser, wd->imported_file); + return cast(isize)err; +} + + void parser_add_file_to_process(Parser *p, AstPackage *pkg, FileInfo fi, TokenPos pos) { + // TODO(bill): Use a better allocator ImportedFile f = {pkg, fi, pos, p->files_to_process.count}; + auto wd = gb_alloc_item(heap_allocator(), ParserWorkerData); + wd->parser = p; + wd->imported_file = f; array_add(&p->files_to_process, f); + thread_pool_add_task(&parser_thread_pool, parser_worker_proc, wd); } @@ -4717,9 +4731,9 @@ bool parse_file(Parser *p, AstFile *f) { } -ParseFileError process_imported_file(Parser *p, ImportedFile imported_file) { +ParseFileError process_imported_file(Parser *p, ImportedFile const &imported_file) { AstPackage *pkg = imported_file.pkg; - FileInfo *fi = &imported_file.fi; + FileInfo const *fi = &imported_file.fi; TokenPos pos = imported_file.pos; AstFile *file = gb_alloc_item(heap_allocator(), AstFile); @@ -4737,35 +4751,32 @@ ParseFileError process_imported_file(Parser *p, ImportedFile imported_file) { error(pos, "Initial file is empty - %.*s\n", LIT(p->init_fullpath)); gb_exit(1); } - goto skip; - } + } else { + switch (err) { + case ParseFile_WrongExtension: + error(pos, "Failed to parse file: %.*s; invalid file extension: File must have the extension '.odin'", LIT(fi->name)); + break; + case ParseFile_InvalidFile: + error(pos, "Failed to parse file: %.*s; invalid file or cannot be found", LIT(fi->name)); + break; + case ParseFile_Permission: + error(pos, "Failed to parse file: %.*s; file permissions problem", LIT(fi->name)); + break; + case ParseFile_NotFound: + error(pos, "Failed to parse file: %.*s; file cannot be found ('%.*s')", LIT(fi->name), LIT(fi->fullpath)); + break; + case ParseFile_InvalidToken: + error(err_pos, "Failed to parse file: %.*s; invalid token found in file", LIT(fi->name)); + break; + case ParseFile_EmptyFile: + error(pos, "Failed to parse file: %.*s; file contains no tokens", LIT(fi->name)); + break; + } - switch (err) { - case ParseFile_WrongExtension: - error(pos, "Failed to parse file: %.*s; invalid file extension: File must have the extension '.odin'", LIT(fi->name)); - break; - case ParseFile_InvalidFile: - error(pos, "Failed to parse file: %.*s; invalid file or cannot be found", LIT(fi->name)); - break; - case ParseFile_Permission: - error(pos, "Failed to parse file: %.*s; file permissions problem", LIT(fi->name)); - break; - case ParseFile_NotFound: - error(pos, "Failed to parse file: %.*s; file cannot be found ('%.*s')", LIT(fi->name), LIT(fi->fullpath)); - break; - case ParseFile_InvalidToken: - error(err_pos, "Failed to parse file: %.*s; invalid token found in file", LIT(fi->name)); - break; - case ParseFile_EmptyFile: - error(pos, "Failed to parse file: %.*s; file contains no tokens", LIT(fi->name)); - break; + return err; } - - return err; } - -skip: if (parse_file(p, file)) { gb_mutex_lock(&p->file_add_mutex); defer (gb_mutex_unlock(&p->file_add_mutex)); @@ -4781,66 +4792,26 @@ skip: p->total_line_count += file->tokenizer.line_count; p->total_token_count += file->tokens.count; } + return ParseFile_None; } -struct ParserWorkerThreadData { - Parser *parser; - ParseFileError err; - - gbSemaphore resume_work; - gbMutex lock; - - bool error_available; - bool is_working; - bool should_exit; -}; - - -GB_THREAD_PROC(parse_worker_file_proc) { - GB_ASSERT(thread != nullptr); - - ParserWorkerThreadData* data = cast(ParserWorkerThreadData*) thread->user_data; - - for(;;) { - gb_semaphore_wait(&data->resume_work); - - gb_mutex_lock(&data->lock); - if (data->should_exit) { - gb_mutex_unlock(&data->lock); - return isize(0); - } - - Parser *p = data->parser; - isize index = thread->user_index; - gb_mutex_lock(&p->file_add_mutex); - auto file_to_process = p->files_to_process[index]; - gb_mutex_unlock(&p->file_add_mutex); - data->err = process_imported_file(p, file_to_process); - - data->error_available = true; - data->is_working = false; - gb_mutex_unlock(&data->lock); - } - - //GB_PANIC("A worker thread should not be able to reach the end!!!"); -} ParseFileError parse_packages(Parser *p, String init_filename) { GB_ASSERT(init_filename.text[init_filename.len] == 0); - // char *fullpath_str = gb_path_get_full_name(heap_allocator(), cast(char const *)&init_filename[0]); - // String init_fullpath = string_trim_whitespace(make_string_c(fullpath_str)); + isize thread_count = gb_max(build_context.thread_count, 1); + thread_pool_init(&parser_thread_pool, heap_allocator(), thread_count, "ParserWork"); + String init_fullpath = path_to_full_path(heap_allocator(), init_filename); if (!path_is_directory(init_fullpath)) { String const ext = str_lit(".odin"); if (!string_ends_with(init_fullpath, ext)) { - gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); + error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); return ParseFile_WrongExtension; } } - TokenPos init_pos = {}; if (!build_context.generate_docs) { String s = get_fullpath_core(heap_allocator(), str_lit("runtime")); @@ -4850,131 +4821,17 @@ ParseFileError parse_packages(Parser *p, String init_filename) { try_add_import_path(p, init_fullpath, init_fullpath, init_pos, Package_Init); p->init_fullpath = init_fullpath; -#if 1 - isize thread_count = gb_max(build_context.thread_count, 1); - if (thread_count > 1) { - isize volatile curr_import_index = 0; - -#if 0 - //NOTE(thebirk): Leaving this piece of code behind if it turns out we need it, yes I know git exists - - isize initial_file_count = p->files_to_process.count; - // NOTE(bill): Make sure that these are in parsed in this order - for (isize i = 0; i < initial_file_count; i++) { - ParseFileError err = process_imported_file(p, p->files_to_process[i]); - if (err != ParseFile_None) { - return err; - } - curr_import_index++; - } -#endif - - auto worker_threads_data = array_make(heap_allocator(), thread_count); - defer (array_free(&worker_threads_data)); - - for_array(i, worker_threads_data) { - ParserWorkerThreadData *data = &worker_threads_data[i]; - gb_mutex_init(&data->lock); - gb_semaphore_init(&data->resume_work); - data->parser = p; - data->err = ParseFile_None; - data->should_exit = false; - data->error_available = false; - data->is_working = false; - } - defer(for_array(i, worker_threads_data) { - ParserWorkerThreadData *data = &worker_threads_data[i]; - gb_mutex_destroy(&data->lock); - gb_semaphore_destroy(&data->resume_work); - }); - - auto worker_threads = array_make(heap_allocator(), thread_count); - defer (array_free(&worker_threads)); - - for_array(i, worker_threads) { - gbThread *t = &worker_threads[i]; - gb_thread_init(t); - //NOTE(thebirk): This crashes on linux. In addition to that the method used on windows does - // not get picked up by a lot of tools look into using SetThreadDescription - // when running on new enough windows 10 builds - //char buffer[32]; - //gb_snprintf(buffer, 32, "Parser Worker #%ll", i); - //gb_thread_set_name(t, buffer); - gb_thread_start(t, parse_worker_file_proc, &worker_threads_data[i]); - } - defer(for_array(i, worker_threads) { - gb_thread_destroy(&worker_threads[i]); - }); - - auto errors = array_make(heap_allocator(), 0, 16); - - for (;;) { - int num_alive = 0; - - for_array(i, worker_threads) { - gbThread *t = &worker_threads[i]; - ParserWorkerThreadData *data = &worker_threads_data[i]; - - if (!data->is_working && gb_mutex_try_lock(&data->lock)) { - if (data->error_available) { - auto curr_err = data->err; - if (curr_err != ParseFile_None) { - array_add(&errors, curr_err); - } - - data->error_available = false; - } - - if (curr_import_index < p->files_to_process.count) { - t->user_index = curr_import_index; - curr_import_index++; - num_alive += 1; + thread_pool_start(&parser_thread_pool); + thread_pool_kick_and_wait(&parser_thread_pool); - gb_semaphore_release(&data->resume_work); - data->is_working = true; - } - - gb_mutex_unlock(&data->lock); - } else { - //NOTE(thebirk): If we cant lock a thread it must be working - num_alive += 1; - } - } - - if ((num_alive == 0) && (curr_import_index >= p->files_to_process.count)) { - break; - } - - gb_yield(); - } - - //NOTE(thebirk): Signal all workers to exit - for_array(i, worker_threads_data) { - ParserWorkerThreadData* data = &worker_threads_data[i]; - data->should_exit = true; - gb_semaphore_release(&data->resume_work); - } - - if (errors.count > 0) { - return errors[errors.count-1]; - } - } else { - for_array(i, p->files_to_process) { - ParseFileError err = process_imported_file(p, p->files_to_process[i]); - if (err != ParseFile_None) { - return err; - } - } - } -#else - for_array(i, p->files_to_process) { - ImportedFile f = p->files_to_process[i]; - ParseFileError err = process_imported_file(p, f); + // NOTE(bill): Get the last error and use that + for (isize i = parser_thread_pool.threads.count-1; i >= 0; i--) { + gbThread *t = &parser_thread_pool.threads[i]; + ParseFileError err = cast(ParseFileError)t->return_value; if (err != ParseFile_None) { return err; } } -#endif return ParseFile_None; } diff --git a/src/parser.hpp b/src/parser.hpp index 26536fe56..56d9b74c7 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -141,6 +141,19 @@ struct Parser { gbMutex file_decl_mutex; }; + +gb_global ThreadPool parser_thread_pool = {}; + +struct ParserWorkerData { + Parser *parser; + ImportedFile imported_file; +}; + + + + + + enum ProcInlining { ProcInlining_none = 0, ProcInlining_inline = 1, -- cgit v1.2.3 From 4afc78efc682152a9b096781a1598fd1841ca938 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 8 Sep 2019 12:12:41 +0100 Subject: Add `where` clauses to `struct` and `union` --- core/odin/ast/ast.odin | 18 +++++++----- core/odin/parser/parser.odin | 42 +++++++++++++++++++++------ src/check_decl.cpp | 2 +- src/check_expr.cpp | 22 ++++++--------- src/check_type.cpp | 5 +++- src/checker.cpp | 2 +- src/parser.cpp | 67 +++++++++++++++++++++++++++++++++----------- src/parser.hpp | 28 ++++++++++-------- 8 files changed, 125 insertions(+), 61 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index f1ea79584..baad5f802 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -568,13 +568,15 @@ Dynamic_Array_Type :: struct { Struct_Type :: struct { using node: Expr, - tok_pos: token.Pos, - poly_params: ^Field_List, - align: ^Expr, - is_packed: bool, - is_raw_union: bool, - fields: ^Field_List, - name_count: int, + tok_pos: token.Pos, + poly_params: ^Field_List, + align: ^Expr, + fields: ^Field_List, + name_count: int, + where_token: token.Token, + where_clauses: []^Expr, + is_packed: bool, + is_raw_union: bool, } Union_Type :: struct { @@ -583,6 +585,8 @@ Union_Type :: struct { poly_params: ^Field_List, align: ^Expr, variants: []^Expr, + where_token: token.Token, + where_clauses: []^Expr, } Enum_Type :: struct { diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index a0d4d639e..f652e2c02 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2174,17 +2174,29 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { error(p, tok.pos, "'#raw_union' cannot also be '#packed"); } + where_token: token.Token; + where_clauses: []^ast.Expr; + if (p.curr_tok.kind == token.Where) { + where_token = expect_token(p, token.Where); + prev_level := p.expr_level; + p.expr_level = -1; + where_clauses = parse_rhs_expr_list(p); + p.expr_level = prev_level; + } + expect_token(p, token.Open_Brace); fields, name_count = parse_field_list(p, token.Close_Brace, ast.Field_Flags_Struct); close := expect_token(p, token.Close_Brace); st := ast.new(ast.Struct_Type, tok.pos, end_pos(close)); - st.poly_params = poly_params; - st.align = align; - st.is_packed = is_packed; - st.is_raw_union = is_raw_union; - st.fields = fields; - st.name_count = name_count; + st.poly_params = poly_params; + st.align = align; + st.is_packed = is_packed; + st.is_raw_union = is_raw_union; + st.fields = fields; + st.name_count = name_count; + st.where_token = where_token; + st.where_clauses = where_clauses; return st; case token.Union: @@ -2217,6 +2229,16 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { } p.expr_level = prev_level; + where_token: token.Token; + where_clauses: []^ast.Expr; + if (p.curr_tok.kind == token.Where) { + where_token = expect_token(p, token.Where); + prev_level := p.expr_level; + p.expr_level = -1; + where_clauses = parse_rhs_expr_list(p); + p.expr_level = prev_level; + } + variants: [dynamic]^ast.Expr; expect_token_after(p, token.Open_Brace, "union"); @@ -2234,9 +2256,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { close := expect_token(p, token.Close_Brace); ut := ast.new(ast.Union_Type, tok.pos, end_pos(close)); - ut.poly_params = poly_params; - ut.variants = variants[:]; - ut.align = align; + ut.poly_params = poly_params; + ut.variants = variants[:]; + ut.align = align; + ut.where_token = where_token; + ut.where_clauses = where_clauses; return ut; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 388b37864..173ba4a90 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1176,7 +1176,7 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty } - bool where_clause_ok = evaluate_where_clauses(ctx, decl, true); + bool where_clause_ok = evaluate_where_clauses(ctx, 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 ca1aed970..8a5ed9c1c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5471,15 +5471,10 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize } -bool evaluate_where_clauses(CheckerContext *ctx, DeclInfo *decl, bool print_err) { - Ast *proc_lit = decl->proc_lit; - GB_ASSERT(proc_lit != nullptr); - GB_ASSERT(proc_lit->kind == Ast_ProcLit); - - if (proc_lit->ProcLit.where_token.kind != Token_Invalid) { - auto &clauses = proc_lit->ProcLit.where_clauses; - for_array(i, clauses) { - Ast *clause = clauses[i]; +bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array *clauses, bool print_err) { + if (clauses != nullptr) { + for_array(i, *clauses) { + Ast *clause = (*clauses)[i]; Operand o = {}; check_expr(ctx, &o, clause); if (o.mode != Addressing_Constant) { @@ -5494,10 +5489,10 @@ bool evaluate_where_clauses(CheckerContext *ctx, DeclInfo *decl, bool print_err) error(clause, "'where' clause evaluated to false:\n\t%s", str); gb_string_free(str); - if (decl->scope != nullptr) { + if (scope != nullptr) { isize print_count = 0; - for_array(j, decl->scope->elements.entries) { - Entity *e = decl->scope->elements.entries[j].value; + for_array(j, scope->elements.entries) { + Entity *e = scope->elements.entries[j].value; switch (e->kind) { case Entity_TypeName: { if (print_count == 0) error_line("\n\tWith the following definitions:\n"); @@ -5790,7 +5785,8 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type ctx.curr_proc_decl = decl; ctx.curr_proc_sig = e->type; - if (!evaluate_where_clauses(&ctx, decl, false)) { + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + if (!evaluate_where_clauses(&ctx, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) { continue; } } diff --git a/src/check_type.cpp b/src/check_type.cpp index 2fa8939c4..e1602365a 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -504,8 +504,8 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< struct_type->Struct.polymorphic_params = polymorphic_params; struct_type->Struct.is_poly_specialized = is_poly_specialized; - if (!is_polymorphic) { + bool where_clause_ok = evaluate_where_clauses(ctx, 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); } @@ -688,6 +688,9 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, ArrayUnion.is_polymorphic = is_polymorphic; union_type->Union.is_poly_specialized = is_poly_specialized; + bool where_clause_ok = evaluate_where_clauses(ctx, ctx->scope, &ut->where_clauses, true); + + for_array(i, ut->variants) { Ast *node = ut->variants[i]; Type *t = check_type_expr(ctx, node, nullptr); diff --git a/src/checker.cpp b/src/checker.cpp index 8fe71b63c..994e0776d 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3157,7 +3157,7 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { Entity *e = scope->elements.entries[elem_index].value; if (e->scope == parent_scope) continue; - if (is_entity_exported(e)) { + if (is_entity_exported(e, true)) { Entity *found = scope_lookup_current(parent_scope, name); if (found != nullptr) { // NOTE(bill): diff --git a/src/parser.cpp b/src/parser.cpp index 123e1fcee..204ff3984 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -349,10 +349,12 @@ Ast *clone_ast(Ast *node) { n->StructType.fields = clone_ast_array(n->StructType.fields); n->StructType.polymorphic_params = clone_ast(n->StructType.polymorphic_params); n->StructType.align = clone_ast(n->StructType.align); + n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses); break; case Ast_UnionType: n->UnionType.variants = clone_ast_array(n->UnionType.variants); n->UnionType.polymorphic_params = clone_ast(n->UnionType.polymorphic_params); + n->UnionType.where_clauses = clone_ast_array(n->UnionType.where_clauses); break; case Ast_EnumType: n->EnumType.base_type = clone_ast(n->EnumType.base_type); @@ -921,7 +923,8 @@ Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { Ast *ast_struct_type(AstFile *f, Token token, Array fields, isize field_count, Ast *polymorphic_params, bool is_packed, bool is_raw_union, - Ast *align) { + Ast *align, + Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_StructType); result->StructType.token = token; result->StructType.fields = fields; @@ -930,17 +933,22 @@ Ast *ast_struct_type(AstFile *f, Token token, Array fields, isize field_c result->StructType.is_packed = is_packed; result->StructType.is_raw_union = is_raw_union; result->StructType.align = align; + result->StructType.where_token = where_token; + result->StructType.where_clauses = where_clauses; return result; } -Ast *ast_union_type(AstFile *f, Token token, Array variants, Ast *polymorphic_params, Ast *align, bool no_nil) { +Ast *ast_union_type(AstFile *f, Token token, Array variants, Ast *polymorphic_params, Ast *align, bool no_nil, + Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_UnionType); result->UnionType.token = token; result->UnionType.variants = variants; result->UnionType.polymorphic_params = polymorphic_params; result->UnionType.align = align; result->UnionType.no_nil = no_nil; + result->UnionType.where_token = where_token; + result->UnionType.where_clauses = where_clauses; return result; } @@ -2020,6 +2028,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(token, "'#raw_union' cannot also be '#packed'"); } + Token where_token = {}; + Array where_clauses = {}; + + if (f->curr_token.kind == Token_where) { + where_token = expect_token(f, Token_where); + isize prev_level = f->expr_level; + f->expr_level = -1; + where_clauses = parse_rhs_expr_list(f); + f->expr_level = prev_level; + } + + Token open = expect_token_after(f, Token_OpenBrace, "struct"); isize name_count = 0; @@ -2032,7 +2052,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { decls = fields->FieldList.list; } - return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, align); + return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, align, where_token, where_clauses); } break; case Token_union: { @@ -2073,6 +2093,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { } } + Token where_token = {}; + Array where_clauses = {}; + + if (f->curr_token.kind == Token_where) { + where_token = expect_token(f, Token_where); + isize prev_level = f->expr_level; + f->expr_level = -1; + where_clauses = parse_rhs_expr_list(f); + f->expr_level = prev_level; + } + + Token open = expect_token_after(f, Token_OpenBrace, "union"); while (f->curr_token.kind != Token_CloseBrace && @@ -2088,7 +2120,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); + return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, where_token, where_clauses); } break; case Token_enum: { @@ -4424,19 +4456,6 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, } - if (is_package_name_reserved(file_str)) { - *path = file_str; - return true; - } - - if (file_mutex) gb_mutex_lock(file_mutex); - defer (if (file_mutex) gb_mutex_unlock(file_mutex)); - - - if (node->kind == Ast_ForeignImportDecl) { - node->ForeignImportDecl.collection_name = collection_name; - } - if (collection_name.len > 0) { if (collection_name == "system") { if (node->kind != Ast_ForeignImportDecl) { @@ -4467,6 +4486,20 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, #endif } + + if (is_package_name_reserved(file_str)) { + *path = file_str; + return true; + } + + if (file_mutex) gb_mutex_lock(file_mutex); + defer (if (file_mutex) gb_mutex_unlock(file_mutex)); + + + if (node->kind == Ast_ForeignImportDecl) { + node->ForeignImportDecl.collection_name = collection_name; + } + if (has_windows_drive) { *path = file_str; } else { diff --git a/src/parser.hpp b/src/parser.hpp index 3da2551f1..419cf9da3 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -491,20 +491,24 @@ AST_KIND(_TypeBegin, "", bool) \ Ast *elem; \ }) \ AST_KIND(StructType, "struct type", struct { \ - Token token; \ - Array fields; \ - isize field_count; \ - Ast *polymorphic_params; \ - Ast *align; \ - bool is_packed; \ - bool is_raw_union; \ + Token token; \ + Array fields; \ + isize field_count; \ + Ast *polymorphic_params; \ + Ast *align; \ + Token where_token; \ + Array where_clauses; \ + bool is_packed; \ + bool is_raw_union; \ }) \ AST_KIND(UnionType, "union type", struct { \ - Token token; \ - Array variants; \ - Ast *polymorphic_params; \ - Ast * align; \ - bool no_nil; \ + Token token; \ + Array variants; \ + Ast *polymorphic_params; \ + Ast * align; \ + bool no_nil; \ + Token where_token; \ + Array where_clauses; \ }) \ AST_KIND(EnumType, "enum type", struct { \ Token token; \ -- cgit v1.2.3 From 68582c5ad1b2bf562242b9d2f40c89efad343b66 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Sep 2019 19:47:04 +0100 Subject: Add suggestions to errors on casts and assignments. --- src/check_decl.cpp | 3 +- src/check_expr.cpp | 93 +++++++++++++++++++++++++++++++++++++++++++++++------- src/check_stmt.cpp | 3 +- 3 files changed, 85 insertions(+), 14 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 173ba4a90..166b6e715 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -112,7 +112,8 @@ void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Ar isize rhs_count = operands.count; for_array(i, operands) { if (operands[i].mode == Addressing_Invalid) { - rhs_count--; + // TODO(bill): Should I ignore invalid parameters? + // rhs_count--; } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8a5ed9c1c..5ef42a3fc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -90,7 +90,7 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCallingConvention cc); bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type *abi_return_type); void set_procedure_abi_types(CheckerContext *c, Type *type); - +void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type); Entity *entity_from_expr(Ast *expr) { expr = unparen_expr(expr); @@ -786,6 +786,7 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co op_type_str, type_str, LIT(context_name)); + check_assignment_error_suggestion(c, operand, type); break; } operand->mode = Addressing_Invalid; @@ -1509,32 +1510,98 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ return false; } + +void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) { + gbString a = expr_to_string(o->expr); + gbString b = type_to_string(type); + defer( + gb_string_free(b); + gb_string_free(a); + ); + + Type *src = base_type(o->type); + Type *dst = base_type(type); + + if (is_type_array(src) && is_type_slice(dst)) { + Type *s = src->Array.elem; + Type *d = dst->Slice.elem; + if (are_types_identical(s, d)) { + error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a); + } + } else if (are_types_identical(src, dst)) { + error_line("\tSuggestion: the expression may be directly casted to type %s\n", b); + } else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) { + error_line("\tSuggestion: a string may be casted to %s\n", a, b); + } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) { + error_line("\tSuggestion: the expression may be casted to %s\n", b); + } +} + +void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) { + gbString a = expr_to_string(o->expr); + gbString b = type_to_string(type); + defer( + gb_string_free(b); + gb_string_free(a); + ); + + Type *src = base_type(o->type); + Type *dst = base_type(type); + + if (is_type_array(src) && is_type_slice(dst)) { + Type *s = src->Array.elem; + Type *d = dst->Slice.elem; + if (are_types_identical(s, d)) { + error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a); + } + } else if (is_type_pointer(o->type) && is_type_integer(type)) { + if (is_type_uintptr(type)) { + error_line("\tSuggestion: a pointer may be directly casted to %s\n", b); + } else { + error_line("\tSuggestion: for a pointer to be casted to an integer, it must be converted to 'uintptr' first\n"); + i64 x = type_size_of(o->type); + i64 y = type_size_of(type); + if (x != y) { + error_line("\tNote: the type of expression and the type of the cast have a different size in bytes, %lld vs %lld\n", x, y); + } + } + } else if (is_type_integer(o->type) && is_type_pointer(type)) { + if (is_type_uintptr(o->type)) { + error_line("\tSuggestion: %a may be directly casted to %s\n", a, b); + } else { + error_line("\tSuggestion: for an integer to be casted to a pointer, it must be converted to 'uintptr' first\n"); + } + } else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) { + error_line("\tSuggestion: a string may be casted to %s\n", a, b); + } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) { + error_line("\tSuggestion: the expression may be casted to %s\n", b); + } +} + + void check_is_expressible(CheckerContext *c, Operand *o, Type *type) { GB_ASSERT(is_type_constant_type(type)); GB_ASSERT(o->mode == Addressing_Constant); if (!check_representable_as_constant(c, o->value, type, &o->value)) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); + defer( + gb_string_free(b); + gb_string_free(a); + o->mode = Addressing_Invalid; + ); + if (is_type_numeric(o->type) && is_type_numeric(type)) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s'", a, b); } else { - #if 0 - gbAllocator ha = heap_allocator(); - String str = big_int_to_string(ha, &o->value.value_integer); - defer (gb_free(ha, str.text)); - error(o->expr, "'%s = %.*s' overflows '%s'", a, LIT(str), b); - #else error(o->expr, "Cannot convert '%s' to '%s'", a, b); - #endif + check_assignment_error_suggestion(c, o, type); } } else { error(o->expr, "Cannot convert '%s' to '%s'", a, b); + check_assignment_error_suggestion(c, o, type); } - - gb_string_free(b); - gb_string_free(a); - o->mode = Addressing_Invalid; } } @@ -2165,6 +2232,8 @@ void check_cast(CheckerContext *c, Operand *x, Type *type) { gb_string_free(to_type); gb_string_free(expr_str); + check_cast_error_suggestion(c, x, type); + x->mode = Addressing_Invalid; return; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6945fb00e..9163fadc2 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1366,7 +1366,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { isize rhs_count = rhs_operands.count; for_array(i, rhs_operands) { if (rhs_operands[i].mode == Addressing_Invalid) { - rhs_count--; + // TODO(bill): Should I ignore invalid parameters? + // rhs_count--; } } -- cgit v1.2.3 From 48ab7f876c51dfb009f8ff53d345bb575ce4fb4c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 Sep 2019 20:52:47 +0100 Subject: Fix Implicit Selector Expressions do not work for parameteric struct parameters. #438 --- src/check_expr.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 5ef42a3fc..5535e6772 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6078,6 +6078,19 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper for_array(i, ce->args) { Ast *arg = ce->args[i]; ast_node(fv, FieldValue, arg); + + if (fv->field->kind == Ast_Ident) { + String name = fv->field->Ident.token.string; + isize index = lookup_polymorphic_record_parameter(original_type, name); + if (index >= 0) { + TypeTuple *params = get_record_polymorphic_params(original_type); + Entity *e = params->variables[i]; + if (e->kind == Entity_Constant) { + check_expr_with_type_hint(c, &operands[i], fv->value, e->type); + } + } + + } check_expr_or_type(c, &operands[i], fv->value); } @@ -6088,7 +6101,17 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper } else { operands = array_make(heap_allocator(), 0, 2*ce->args.count); - check_unpack_arguments(c, nullptr, -1, &operands, ce->args, false, false); + + Entity **lhs = nullptr; + isize lhs_count = -1; + + TypeTuple *params = get_record_polymorphic_params(original_type); + if (params != nullptr) { + lhs = params->variables.data; + lhs_count = params->variables.count; + } + + check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, false); } CallArgumentError err = CallArgumentError_None; @@ -6217,6 +6240,9 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper } score += s; } + + // NOTE(bill): Add type info the parameters + add_type_info_type(c, o->type); } { -- cgit v1.2.3 From dae514a2c99875db0dbda56309c7349a68432e19 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Oct 2019 09:40:05 +0100 Subject: Fix Using any in if statement asserts compiler #441 --- 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 5535e6772..697abef5f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1580,8 +1580,10 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) { void check_is_expressible(CheckerContext *c, Operand *o, Type *type) { - GB_ASSERT(is_type_constant_type(type)); GB_ASSERT(o->mode == Addressing_Constant); + if (!is_type_constant_type(type)) { + return; + } if (!check_representable_as_constant(c, o->value, type, &o->value)) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); -- cgit v1.2.3 From 6c69e8c043e7dcf9d9965c7b28c7bdae44e0537c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 6 Oct 2019 14:55:25 +0100 Subject: Make `typeid` semantics consistent across variables and constants --- src/check_decl.cpp | 54 ++++++++++++++++++++++++++++++++--------------- src/check_expr.cpp | 61 +++++++++++++++++++++++++++++++++++++++++------------ src/checker.cpp | 16 ++++++-------- src/exact_value.cpp | 18 ++++++++++++++++ src/ir.cpp | 5 +++++ src/parser.cpp | 4 ---- 6 files changed, 113 insertions(+), 45 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 166b6e715..5e8479a7f 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -41,11 +41,20 @@ Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, Stri } if (operand->mode == Addressing_Type) { - gbString t = type_to_string(operand->type); - error(operand->expr, "Cannot assign a type '%s' to variable '%.*s'", t, LIT(e->token.string)); - gb_string_free(t); - e->type = operand->type; - return nullptr; + if (e->type != nullptr && is_type_typeid(e->type)) { + add_type_info_type(ctx, operand->type); + add_type_and_value(ctx->info, operand->expr, Addressing_Value, e->type, exact_value_typeid(operand->type)); + return e->type; + } else { + gbString t = type_to_string(operand->type); + defer (gb_string_free(t)); + error(operand->expr, "Cannot assign a type '%s' to variable '%.*s'", t, LIT(e->token.string)); + if (e->type == nullptr) { + error_line("\tThe type of the variable '%.*s' cannot be inferred as a type does not have a type\n", LIT(e->token.string)); + } + e->type = operand->type; + return nullptr; + } } @@ -240,7 +249,7 @@ isize total_attribute_count(DeclInfo *decl) { } -void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) { +void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) { GB_ASSERT(e->type == nullptr); DeclInfo *decl = decl_info_of_entity(e); @@ -248,9 +257,8 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) check_decl_attributes(ctx, decl->attributes, const_decl_attribute, nullptr); } - - bool is_distinct = is_type_distinct(type_expr); - Ast *te = remove_type_alias_clutter(type_expr); + bool is_distinct = is_type_distinct(init_expr); + Ast *te = remove_type_alias_clutter(init_expr); e->type = t_invalid; String name = e->token.string; Type *named = alloc_type_named(name, nullptr, e); @@ -266,7 +274,7 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) named->Named.base = base_type(bt); if (is_distinct && is_type_typeid(e->type)) { - error(type_expr, "'distinct' cannot be applied to 'typeid'"); + error(init_expr, "'distinct' cannot be applied to 'typeid'"); is_distinct = false; } if (!is_distinct) { @@ -275,6 +283,19 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) e->TypeName.is_type_alias = true; } + + if (decl->type_expr != nullptr) { + Type *t = check_type(ctx, decl->type_expr); + if (t != nullptr && !is_type_typeid(t)) { + Operand operand = {}; + operand.mode = Addressing_Type; + operand.type = e->type; + operand.expr = init_expr; + check_assignment(ctx, &operand, t, str_lit("constant declaration")); + } + } + + // using decl if (decl->is_using) { // NOTE(bill): Must be an enum declaration @@ -363,15 +384,14 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, switch (operand.mode) { case Addressing_Type: { + if (e->type != nullptr && !is_type_typeid(e->type)) { + check_assignment(ctx, &operand, e->type, str_lit("constant declaration")); + } + e->kind = Entity_TypeName; e->type = nullptr; - DeclInfo *d = ctx->decl; - if (d->type_expr != nullptr) { - error(e->token, "A type declaration cannot have an type parameter"); - } - d->type_expr = d->init_expr; - check_type_decl(ctx, e, d->type_expr, named_type); + check_type_decl(ctx, e, ctx->decl->init_expr, named_type); return; } @@ -1070,7 +1090,7 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_ check_const_decl(&c, e, d->type_expr, d->init_expr, named_type); break; case Entity_TypeName: { - check_type_decl(&c, e, d->type_expr, named_type); + check_type_decl(&c, e, d->init_expr, named_type); break; } case Entity_Procedure: diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 697abef5f..6a0d8221f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -442,6 +442,10 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type } if (operand->mode == Addressing_Type) { + if (is_type_typeid(type)) { + add_type_info_type(c, operand->type); + return 4; + } return -1; } @@ -755,7 +759,12 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co return; } - if (!check_is_assignable_to(c, operand, type)) { + if (check_is_assignable_to(c, operand, type)) { + if (operand->mode == Addressing_Type && is_type_typeid(type)) { + add_type_info_type(c, operand->type); + add_type_and_value(c->info, operand->expr, Addressing_Value, type, exact_value_typeid(operand->type)); + } + } else { gbString expr_str = expr_to_string(operand->expr); gbString op_type_str = type_to_string(operand->type); gbString type_str = type_to_string(type); @@ -1734,6 +1743,23 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { return; } + if (x->mode == Addressing_Type && is_type_typeid(y->type)) { + add_type_info_type(c, x->type); + add_type_and_value(c->info, x->expr, Addressing_Value, y->type, exact_value_typeid(x->type)); + + x->mode = Addressing_Value; + x->type = t_untyped_bool; + return; + } else if (is_type_typeid(x->type) && y->mode == Addressing_Type) { + add_type_info_type(c, y->type); + add_type_and_value(c->info, y->expr, Addressing_Value, x->type, exact_value_typeid(y->type)); + + x->mode = Addressing_Value; + x->type = t_untyped_bool; + return; + } + + gbString err_str = nullptr; defer (if (err_str != nullptr) { @@ -2324,8 +2350,16 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint // If only one is a type, this is an error if (xt ^ yt) { GB_ASSERT(xt != yt); - if (xt) error_operand_not_expression(x); - if (yt) error_operand_not_expression(y); + if (xt) { + if (!is_type_typeid(y->type)) { + error_operand_not_expression(x); + } + } + if (yt) { + if (!is_type_typeid(x->type)) { + error_operand_not_expression(y); + } + } } break; @@ -5254,6 +5288,11 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } } score += s; + + 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)); + } } if (variadic) { @@ -5496,6 +5535,11 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { } score += s; } + + 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)); + } } if (data) { @@ -6432,17 +6476,6 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { } } - // NOTE(bill): Should this be here or on the `add_entity_use`? - // if (ce->proc != nullptr) { - // Entity *e = entity_of_node(&c->info, ce->proc); - // if (e != nullptr && e->kind == Entity_Procedure) { - // String msg = e->Procedure.deprecated_message; - // if (msg.len > 0) { - // warning(call, "%.*s is deprecated: %.*s", LIT(e->token.string), LIT(msg)); - // } - // } - // } - CallArgumentData data = check_call_arguments(c, operand, proc_type, call); Type *result_type = data.result_type; gb_zero_item(operand); diff --git a/src/checker.cpp b/src/checker.cpp index 994e0776d..f30273439 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2613,14 +2613,14 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { Entity *e = nullptr; d->attributes = vd->attributes; + d->type_expr = vd->type; + d->init_expr = init; if (is_ast_type(init)) { e = alloc_entity_type_name(d->scope, token, nullptr); - if (vd->type != nullptr) { - error(name, "A type declaration cannot have an type parameter"); - } - d->type_expr = init; - d->init_expr = init; + // if (vd->type != nullptr) { + // error(name, "A type declaration cannot have an type parameter"); + // } } else if (init->kind == Ast_ProcLit) { if (c->scope->flags&ScopeFlag_Type) { error(name, "Procedure declarations are not allowed within a struct"); @@ -2647,19 +2647,15 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { pl->type->ProcType.calling_convention = cc; } d->proc_lit = init; - d->type_expr = vd->type; + d->init_expr = init; } else if (init->kind == Ast_ProcGroup) { ast_node(pg, ProcGroup, init); e = alloc_entity_proc_group(d->scope, token, nullptr); if (fl != nullptr) { error(name, "Procedure groups are not allowed within a foreign block"); } - d->init_expr = init; - d->type_expr = vd->type; } else { e = alloc_entity_constant(d->scope, token, nullptr, empty_exact_value); - d->type_expr = vd->type; - d->init_expr = init; } e->identifier = name; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index fcc6f1973..95d04d93b 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -38,6 +38,7 @@ enum ExactValueKind { ExactValue_Pointer, ExactValue_Compound, // TODO(bill): Is this good enough? ExactValue_Procedure, // TODO(bill): Is this good enough? + ExactValue_Typeid, ExactValue_Count, }; @@ -54,6 +55,7 @@ struct ExactValue { Quaternion256 value_quaternion; Ast * value_compound; Ast * value_procedure; + Type * value_typeid; }; }; @@ -82,6 +84,8 @@ HashKey hash_exact_value(ExactValue v) { return hash_pointer(v.value_compound); case ExactValue_Procedure: return hash_pointer(v.value_procedure); + case ExactValue_Typeid: + return hash_pointer(v.value_typeid); } return hashing_proc(&v, gb_size_of(ExactValue)); @@ -154,6 +158,13 @@ ExactValue exact_value_procedure(Ast *node) { } +ExactValue exact_value_typeid(Type *type) { + ExactValue result = {ExactValue_Typeid}; + result.value_typeid = type; + return result; +} + + ExactValue exact_value_integer_from_string(String const &string) { ExactValue result = {ExactValue_Integer}; big_int_from_string(&result.value_integer, string); @@ -889,6 +900,13 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { } break; } + + case ExactValue_Typeid: + switch (op) { + case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid); + case Token_NotEq: return !are_types_identical(x.value_typeid, y.value_typeid); + } + break; } GB_PANIC("Invalid comparison"); diff --git a/src/ir.cpp b/src/ir.cpp index 758267c24..f29d389f4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6592,6 +6592,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { return ir_emit_conv(proc, x, tv.type); } + if (tv.value.kind == ExactValue_Typeid) { + irValue *v = ir_typeid(proc->module, tv.value.value_typeid); + return ir_emit_conv(proc, v, tv.type); + } + return ir_add_module_constant(proc->module, tv.type, tv.value); } diff --git a/src/parser.cpp b/src/parser.cpp index 204ff3984..fb093ffd9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1923,10 +1923,6 @@ Ast *parse_operand(AstFile *f, bool lhs) { case Token_typeid: { Token token = expect_token(f, Token_typeid); - // Ast *specialization = nullptr; - // if (allow_token(f, Token_Quo)) { - // specialization = parse_type(f); - // } return ast_typeid_type(f, token, nullptr); } break; -- cgit v1.2.3 From 2a6d9e8927ad1eb1e5f3a79fc9ed068a02cbfdfc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 13 Oct 2019 12:38:23 +0100 Subject: `#panic`; Minor change to demo.odin; Fix `#assert` bug at file scope --- core/odin/parser/parser.odin | 2 +- core/runtime/core.odin | 54 +++++++++++++++++++----------------- examples/demo/demo.odin | 20 +++++++------- src/check_expr.cpp | 22 +++++++++++++-- src/check_type.cpp | 37 ++++++++++++++++++++----- src/checker.cpp | 13 +++++++-- src/checker.hpp | 1 + src/main.cpp | 14 ++++------ src/parser.cpp | 38 +++---------------------- src/types.cpp | 66 ++++++++++++++++++++++++-------------------- 10 files changed, 145 insertions(+), 122 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 213041924..fee9b7bdc 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1091,7 +1091,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { case: error(p, stmt.pos, "#complete can only be applied to a switch statement"); } return stmt; - case "assert": + case "assert", "panic": bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(tag)); bd.tok = tok; bd.name = name; diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 729932781..9ad5d3d8a 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -6,6 +6,7 @@ package runtime import "core:os" import "core:mem" import "core:log" +import "intrinsics" // Naming Conventions: // In general, Ada_Case for types and snake_case for values @@ -187,12 +188,13 @@ Typeid_Kind :: enum u8 { #assert(len(Typeid_Kind) < 32); Typeid_Bit_Field :: bit_field #align align_of(uintptr) { - index: 8*size_of(align_of(uintptr)) - 8, + index: 8*size_of(uintptr) - 8, kind: 5, // Typeid_Kind named: 1, special: 1, // signed, cstring, etc reserved: 1, } +#assert(size_of(Typeid_Bit_Field) == size_of(uintptr)); // NOTE(bill): only the ones that are needed (not all types) // This will be set by the compiler @@ -678,7 +680,7 @@ assert :: proc(condition: bool, message := "", loc := #caller_location) -> bool if p == nil { p = default_assertion_failure_proc; } - p("Runtime assertion", message, loc); + p("runtime assertion", message, loc); }(message, loc); } return condition; @@ -690,7 +692,7 @@ panic :: proc(message: string, loc := #caller_location) -> ! { if p == nil { p = default_assertion_failure_proc; } - p("Panic", message, loc); + p("panic", message, loc); } @builtin @@ -816,8 +818,7 @@ __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { value: V, }; - _, is_string := type_info_base(type_info_of(K)).variant.(Type_Info_String); - header.is_key_string = is_string; + header.is_key_string = intrinsics.type_is_string(K); header.entry_size = int(size_of(Entry)); header.entry_align = int(align_of(Entry)); header.value_offset = uintptr(offset_of(Entry, value)); @@ -828,33 +829,34 @@ __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { __get_map_key :: proc "contextless" (k: $K) -> Map_Key { key := k; map_key: Map_Key; - ti := type_info_base_without_enum(type_info_of(K)); - switch _ in ti.variant { - case Type_Info_Integer: - switch 8*size_of(key) { - case 8: map_key.hash = u64(( ^u8)(&key)^); - case 16: map_key.hash = u64(( ^u16)(&key)^); - case 32: map_key.hash = u64(( ^u32)(&key)^); - case 64: map_key.hash = u64(( ^u64)(&key)^); - case: panic("Unhandled integer size"); - } - case Type_Info_Rune: + + T :: intrinsics.type_core_type(K); + + when intrinsics.type_is_integer(T) { + sz :: 8*size_of(T); + when sz == 8 do map_key.hash = u64(( ^u8)(&key)^); + else when sz == 16 do map_key.hash = u64((^u16)(&key)^); + else when sz == 32 do map_key.hash = u64((^u32)(&key)^); + else when sz == 64 do map_key.hash = u64((^u64)(&key)^); + else do #assert(false, "Unhandled integer size"); + } else when intrinsics.type_is_rune(T) { map_key.hash = u64((^rune)(&key)^); - case Type_Info_Pointer: + } else when intrinsics.type_is_pointer(T) { map_key.hash = u64(uintptr((^rawptr)(&key)^)); - case Type_Info_Float: - switch 8*size_of(key) { - case 32: map_key.hash = u64((^u32)(&key)^); - case 64: map_key.hash = u64((^u64)(&key)^); - case: panic("Unhandled float size"); - } - case Type_Info_String: + } else when intrinsics.type_is_float(T) { + sz :: 8*size_of(T); + when sz == 32 do map_key.hash = u64((^u32)(&key)^); + else when sz == 64 do map_key.hash = u64((^u64)(&key)^); + else do #assert(false, "Unhandled float size"); + } else when intrinsics.type_is_string(T) { + #assert(T == string); str := (^string)(&key)^; map_key.hash = default_hash_string(str); map_key.str = str; - case: - panic("Unhandled map key type"); + } else { + #assert(false, "Unhandled map key type"); } + return map_key; } diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index ff861b1de..5ee9ded84 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -28,9 +28,9 @@ when os.OS == "windows" { Answers to common questions about Odin. */ -@(link_name="general_stuff") -general_stuff :: proc() { - fmt.println("# general_stuff"); +@(link_name="extra_general_stuff") +extra_general_stuff :: proc() { + fmt.println("# extra_general_stuff"); { // `do` for inline statements rather than block foo :: proc() do fmt.println("Foo!"); if false do foo(); @@ -209,8 +209,8 @@ union_type :: proc() { } } - Vector3 :: struct {x, y, z: f32}; - Quaternion :: struct {x, y, z, w: f32}; + Vector3 :: distinct [3]f32; + Quaternion :: distinct quaternion128; // More realistic examples { @@ -320,17 +320,17 @@ union_type :: proc() { /* Entity :: struct { - .. + ... derived: union{^Frog, ^Monster}, } Frog :: struct { using entity: Entity, - .. + ... } Monster :: struct { using entity: Entity, - .. + ... } new_entity :: proc(T: type) -> ^Entity { @@ -539,7 +539,7 @@ parametric_polymorphism :: proc() { fmt.println(r); r = 123; fmt.println(r); - r = Error.Foo0; + r = Error.Foo0; // r = .Foo0; is allow too, see implicit selector expressions below fmt.println(r); } @@ -1190,7 +1190,7 @@ where_clauses :: proc() { main :: proc() { when true { - general_stuff(); + extra_general_stuff(); union_type(); parametric_polymorphism(); threading_example(); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6a0d8221f..7c418c4f0 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3071,7 +3071,6 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ } if (selector->kind != Ast_Ident) { - // if (selector->kind != Ast_Ident) { error(selector, "Illegal selector kind: '%.*s'", LIT(ast_strings[selector->kind])); operand->mode = Addressing_Invalid; operand->expr = node; @@ -3544,6 +3543,25 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->type = t_untyped_bool; operand->mode = Addressing_Constant; + } else if (name == "panic") { + if (ce->args.count != 1) { + error(call, "'#panic' expects 1 argument, got %td", ce->args.count); + return false; + } + if (!is_type_string(operand->type) && operand->mode != Addressing_Constant) { + gbString str = expr_to_string(ce->args[0]); + error(call, "'%s' is not a constant string", str); + gb_string_free(str); + return false; + } + error(call, "Compile time panic: %.*s", LIT(operand->value.value_string)); + if (c->proc_name != "") { + gbString str = type_to_string(c->curr_proc_sig); + error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str); + gb_string_free(str); + } + operand->type = t_invalid; + operand->mode = Addressing_NoValue; } else if (name == "defined") { if (ce->args.count != 1) { error(call, "'#defined' expects 1 argument, got %td", ce->args.count); @@ -6349,7 +6367,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { ce->proc->kind == Ast_BasicDirective) { ast_node(bd, BasicDirective, ce->proc); String name = bd->name; - if (name == "location" || name == "assert" || name == "defined" || name == "load") { + if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "load") { operand->mode = Addressing_Builtin; operand->builtin_id = BuiltinProc_DIRECTIVE; operand->expr = ce->proc; diff --git a/src/check_type.cpp b/src/check_type.cpp index 329755ea5..4d107a9ac 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1847,7 +1847,7 @@ Array systemv_distribute_struct_fields(Type *t) { } auto distributed = array_make(heap_allocator(), 0, distributed_cap); - + i64 sz = type_size_of(bt); switch (bt->kind) { case Type_Basic: switch (bt->Basic.kind){ @@ -1925,9 +1925,9 @@ Array systemv_distribute_struct_fields(Type *t) { array_add(&distributed, t_int); break; + case Type_Union: case Type_DynamicArray: case Type_Map: - case Type_Union: case Type_BitField: // TODO(bill): Ignore? // NOTE(bill, 2019-10-10): Odin specific, don't worry about C calling convention yet goto DEFAULT; @@ -1937,7 +1937,7 @@ Array systemv_distribute_struct_fields(Type *t) { case Type_SimdVector: // TODO(bill): Is this correct logic? default: DEFAULT:; - if (type_size_of(bt) > 0) { + if (sz > 0) { array_add(&distributed, bt); } break; @@ -1959,13 +1959,22 @@ Type *handle_single_distributed_type_parameter(Array const &types, bool if (types.count == 1) { if (offset) *offset = 1; + + i64 sz = type_size_of(types[0]); + if (is_type_float(types[0])) { return types[0]; - } else if (type_size_of(types[0]) == 8) { + } + switch (sz) { + case 0: + GB_PANIC("Zero sized type found!"); + case 1: + case 2: + case 4: + case 8: return types[0]; - } else { - return t_u64; } + return t_u64; } else if (types.count >= 2) { if (types[0] == t_f32 && types[1] == t_f32) { if (offset) *offset = 2; @@ -2050,7 +2059,7 @@ Type *handle_struct_system_v_amd64_abi_type(Type *t) { Type *final_type = nullptr; if (field_types.count == 0) { - return t; + final_type = t; } else if (field_types.count == 1) { final_type = field_types[0]; } else { @@ -2072,8 +2081,22 @@ Type *handle_struct_system_v_amd64_abi_type(Type *t) { variables[1] = alloc_entity_param(nullptr, empty_token, two_types[1], false, false); final_type = alloc_type_tuple(); final_type->Tuple.variables = variables; + if (t->kind == Type_Struct) { + // NOTE(bill): Make this packed + final_type->Tuple.is_packed = t->Struct.is_packed; + } } } + + + GB_ASSERT(final_type != nullptr); + i64 ftsz = type_size_of(final_type); + i64 otsz = type_size_of(original_type); + if (ftsz != otsz) { + // TODO(bill): Handle this case which will be caused by #packed most likely + GB_PANIC("Incorrectly handled case for handle_struct_system_v_amd64_abi_type, %lld vs %lld", ftsz, otsz); + } + return final_type; } } diff --git a/src/checker.cpp b/src/checker.cpp index f30273439..8ce9d8ec2 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3367,8 +3367,9 @@ bool collect_file_decls(CheckerContext *ctx, Array const &decls) { if (es->expr->kind == Ast_CallExpr) { ast_node(ce, CallExpr, es->expr); if (ce->proc->kind == Ast_BasicDirective) { - Operand o = {}; - check_expr(ctx, &o, es->expr); + if (ctx->collect_delayed_decls) { + array_add(&ctx->scope->delayed_directives, es->expr); + } } } case_end; @@ -3525,12 +3526,18 @@ void check_import_entities(Checker *c) { for_array(i, pkg->files) { AstFile *f = pkg->files[i]; CheckerContext ctx = c->init_ctx; - add_curr_ast_file(&ctx, f); + for_array(j, f->scope->delayed_imports) { Ast *decl = f->scope->delayed_imports[j]; check_add_import_decl(&ctx, decl); } + } + for_array(i, pkg->files) { + AstFile *f = pkg->files[i]; + CheckerContext ctx = c->init_ctx; + add_curr_ast_file(&ctx, f); + for_array(j, f->scope->delayed_directives) { Ast *expr = f->scope->delayed_directives[j]; Operand o = {}; diff --git a/src/checker.hpp b/src/checker.hpp index 58cb01a82..f86acce5c 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -222,6 +222,7 @@ struct ForeignContext { typedef Array CheckerTypePath; typedef Array CheckerPolyPath; + // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { Map untyped; // Key: Ast * | Expression -> ExprInfo diff --git a/src/main.cpp b/src/main.cpp index b294e7f96..78859e04e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -445,7 +445,7 @@ bool parse_build_flags(Array args) { path = substring(path, 0, string_extension_position(path)); } #endif - build_context.out_filepath = path; + build_context.out_filepath = path_to_full_path(heap_allocator(), path); } else { gb_printf_err("Invalid -out path, got %.*s\n", LIT(path)); bad_flags = true; @@ -624,7 +624,7 @@ bool parse_build_flags(Array args) { break; } - if (str == "dll") { + if (str == "dll" || str == "shared") { build_context.is_dll = true; } else if (str == "exe") { build_context.is_dll = false; @@ -1112,7 +1112,7 @@ int main(int arg_count, char **arg_ptr) { if (0) { #ifdef GB_SYSTEM_UNIX } else if (selected_target_metrics->metrics == &target_essence_amd64) { - system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s", + system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s", LIT(output_base), LIT(output_base), LIT(build_context.link_flags)); #endif } else { @@ -1239,10 +1239,7 @@ int main(int arg_count, char **arg_ptr) { // This allows you to specify '-f' in a #foreign_system_library, // without having to implement any new syntax specifically for MacOS. #if defined(GB_SYSTEM_OSX) - if (lib.len > 2 && lib[0] == '-' && lib[1] == 'f') { - // framework thingie - lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", (int)(lib.len) - 2, lib.text + 2); - } else if (string_ends_with(lib, str_lit(".framework"))) { + if (string_ends_with(lib, str_lit(".framework"))) { // framework thingie String lib_name = lib; lib_name = remove_extension_from_path(lib_name); @@ -1251,8 +1248,7 @@ int main(int arg_count, char **arg_ptr) { // static libs, absolute full path relative to the file in which the lib was imported from lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); } else if (string_ends_with(lib, str_lit(".dylib"))) { - // dynamic lib, relative path to executable - // lib_str = gb_string_append_fmt(lib_str, " -l:%s/%.*s ", cwd, LIT(lib)); + // dynamic lib lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); } else { // dynamic or static system lib, just link regularly searching system library paths diff --git a/src/parser.cpp b/src/parser.cpp index 025a181ba..f69efc0ce 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3970,34 +3970,6 @@ Ast *parse_stmt(AstFile *f) { return s; } - // case Token_static: { - // CommentGroup *docs = f->lead_comment; - // Token token = expect_token(f, Token_static); - - // Ast *decl = nullptr; - // Array list = parse_lhs_expr_list(f); - // if (list.count == 0) { - // syntax_error(token, "Illegal use of 'static' statement"); - // expect_semicolon(f, nullptr); - // return ast_bad_stmt(f, token, f->curr_token); - // } - - // expect_token_after(f, Token_Colon, "identifier list"); - // decl = parse_value_decl(f, list, docs); - - // if (decl != nullptr && decl->kind == Ast_ValueDecl) { - // if (decl->ValueDecl.is_mutable) { - // decl->ValueDecl.is_static = true; - // } else { - // error(token, "'static' may only be currently used with variable declaration"); - // } - // return decl; - // } - - // syntax_error(token, "Illegal use of 'static' statement"); - // return ast_bad_stmt(f, token, f->curr_token); - // } break; - case Token_using: { CommentGroup *docs = f->lead_comment; Token token = expect_token(f, Token_using); @@ -4071,12 +4043,10 @@ Ast *parse_stmt(AstFile *f) { } else if (tag == "assert") { Ast *t = ast_basic_directive(f, hash_token, tag); return ast_expr_stmt(f, parse_call_expr(f, t)); - } /* else if (name.string == "no_deferred") { - s = parse_stmt(f); - s->stmt_state_flags |= StmtStateFlag_no_deferred; - } */ - - if (tag == "include") { + } else if (tag == "panic") { + Ast *t = ast_basic_directive(f, hash_token, tag); + return ast_expr_stmt(f, parse_call_expr(f, t)); + } else if (tag == "include") { syntax_error(token, "#include is not a valid import declaration kind. Did you mean 'import'?"); s = ast_bad_stmt(f, token, f->curr_token); } else { diff --git a/src/types.cpp b/src/types.cpp index d8a579510..8ad352f62 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -113,21 +113,22 @@ struct BasicType { struct TypeStruct { Array fields; Array tags; - Ast *node; - Scope * scope; - - Array offsets; - bool are_offsets_set; - bool are_offsets_being_processed; - bool is_packed; - bool is_raw_union; - bool is_polymorphic; - bool is_poly_specialized; + Array offsets; + Ast * node; + Scope * scope; + Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; - i64 custom_align; // NOTE(bill): Only used in structs at the moment + i64 custom_align; Entity * names; + + bool are_offsets_set; + bool are_offsets_being_processed; + bool is_packed; + bool is_raw_union; + bool is_polymorphic; + bool is_poly_specialized; }; struct TypeUnion { @@ -137,12 +138,11 @@ struct TypeUnion { i64 variant_block_size; i64 custom_align; i64 tag_size; + Type * polymorphic_params; // Type_Tuple + Type * polymorphic_parent; bool no_nil; - - bool is_polymorphic; - bool is_poly_specialized; - Type * polymorphic_params; // Type_Tuple - Type * polymorphic_parent; + bool is_polymorphic; + bool is_poly_specialized; }; #define TYPE_KINDS \ @@ -190,7 +190,9 @@ struct TypeUnion { TYPE_KIND(Tuple, struct { \ Array variables; /* Entity_Variable */ \ Array offsets; \ + bool are_offsets_being_processed; \ bool are_offsets_set; \ + bool is_packed; \ }) \ TYPE_KIND(Proc, struct { \ Ast *node; \ @@ -201,9 +203,8 @@ struct TypeUnion { i32 result_count; \ Array abi_compat_params; \ Type * abi_compat_result_type; \ - bool return_by_pointer; \ - bool variadic; \ i32 variadic_index; \ + bool variadic; \ bool require_results; \ bool c_vararg; \ bool is_polymorphic; \ @@ -211,6 +212,7 @@ struct TypeUnion { bool has_proc_default_values; \ bool has_named_results; \ bool diverging; /* no return */ \ + bool return_by_pointer; \ u64 tags; \ isize specialization_count; \ ProcCallingConvention calling_convention; \ @@ -1782,7 +1784,8 @@ bool are_types_identical(Type *x, Type *y) { case Type_Tuple: if (y->kind == Type_Tuple) { - if (x->Tuple.variables.count == y->Tuple.variables.count) { + if (x->Tuple.variables.count == y->Tuple.variables.count && + x->Tuple.is_packed == y->Tuple.is_packed) { for_array(i, x->Tuple.variables) { Entity *xe = x->Tuple.variables[i]; Entity *ye = y->Tuple.variables[i]; @@ -2231,19 +2234,22 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty if (type->Array.count <= 4) { // HACK(bill): Memory leak switch (type->Array.count) { - #define _ARRAY_FIELD_CASE(_length, _name) \ - case (_length): \ - if (field_name == _name) { \ + #define _ARRAY_FIELD_CASE_IF(_length, _name) \ + if (field_name == (_name)) { \ selection_add_index(&sel, (_length)-1); \ sel.entity = alloc_entity_array_elem(nullptr, make_token_ident(str_lit(_name)), type->Array.elem, (_length)-1); \ return sel; \ - } \ + } + #define _ARRAY_FIELD_CASE(_length, _name0, _name1) \ + case (_length): \ + _ARRAY_FIELD_CASE_IF(_length, _name0); \ + _ARRAY_FIELD_CASE_IF(_length, _name1); \ /*fallthrough*/ - _ARRAY_FIELD_CASE(4, "w"); - _ARRAY_FIELD_CASE(3, "z"); - _ARRAY_FIELD_CASE(2, "y"); - _ARRAY_FIELD_CASE(1, "x"); + _ARRAY_FIELD_CASE(4, "w", "a"); + _ARRAY_FIELD_CASE(3, "z", "b"); + _ARRAY_FIELD_CASE(2, "y", "g"); + _ARRAY_FIELD_CASE(1, "x", "r"); default: break; #undef _ARRAY_FIELD_CASE @@ -2590,9 +2596,9 @@ bool type_set_offsets(Type *t) { } } else if (is_type_tuple(t)) { if (!t->Tuple.are_offsets_set) { - t->Struct.are_offsets_being_processed = true; - t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, false, false); - t->Struct.are_offsets_being_processed = false; + t->Tuple.are_offsets_being_processed = true; + t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false); + t->Tuple.are_offsets_being_processed = false; t->Tuple.are_offsets_set = true; return true; } -- cgit v1.2.3 From 0bdc3b4f21eca0ff15d5053348711f4b60b7baab Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 15 Oct 2019 17:54:37 +0100 Subject: Fix Values coerce to typeid #443 --- src/check_expr.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7c418c4f0..f8bb88595 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -676,17 +676,6 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co return; } - #if 0 - if (operand->mode == Addressing_Type) { - Type *t = base_type(type); - if (t->kind == Type_Pointer && - t->Pointer.elem == t_type_info) { - add_type_info_type(c, type); - return; - } - } - #endif - if (is_type_untyped(operand->type)) { Type *target_type = type; if (type == nullptr || is_type_any(type)) { @@ -1590,10 +1579,7 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) { void check_is_expressible(CheckerContext *c, Operand *o, Type *type) { GB_ASSERT(o->mode == Addressing_Constant); - if (!is_type_constant_type(type)) { - return; - } - if (!check_representable_as_constant(c, o->value, type, &o->value)) { + if (!is_type_constant_type(type) || !check_representable_as_constant(c, o->value, type, &o->value)) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); defer( -- cgit v1.2.3 From 12ae5ed09e3e798147d2f940cabfbdcead638272 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 15 Oct 2019 20:47:27 +0100 Subject: Fix missing typeid conversion case for variadic parameters --- src/check_expr.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f8bb88595..44bbc8988 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5339,6 +5339,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (is_type_any(elem)) { add_type_info_type(c, o.type); } + if (o.mode == Addressing_Type && is_type_typeid(t)) { + add_type_info_type(c, o.type); + add_type_and_value(c->info, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); + } } } } -- cgit v1.2.3 From 2afe4bea67ff50c2ad41ff7c3ba7fbdc18748746 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 15 Oct 2019 22:43:04 +0100 Subject: Add `instrincs.type_is_valid_map_key` --- src/check_expr.cpp | 2 ++ src/checker_builtin_procs.hpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 44bbc8988..b2a2528dd 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3317,6 +3317,7 @@ BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_end - BuiltinProc__ty is_type_sliceable, is_type_simple_compare, is_type_dereferenceable, + is_type_valid_for_keys, is_type_named, is_type_pointer, @@ -4899,6 +4900,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_type_is_sliceable: case BuiltinProc_type_is_simple_compare: case BuiltinProc_type_is_dereferenceable: + case BuiltinProc_type_is_valid_map_key: case BuiltinProc_type_is_named: case BuiltinProc_type_is_pointer: case BuiltinProc_type_is_opaque: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index b119452d4..50d27d715 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -134,6 +134,7 @@ BuiltinProc__type_begin, BuiltinProc_type_is_sliceable, BuiltinProc_type_is_simple_compare, // easily compared using memcmp BuiltinProc_type_is_dereferenceable, + BuiltinProc_type_is_valid_map_key, BuiltinProc_type_is_named, BuiltinProc_type_is_pointer, @@ -291,8 +292,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_ordered_numeric"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_indexable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_sliceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("type_is_simple_compare"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics,}, + {STR_LIT("type_is_simple_compare"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_dereferenceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_valid_map_key"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_named"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, -- cgit v1.2.3 From f12ded54f2b1a390f556e67a17ff0bf4c301a8e3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 20 Oct 2019 13:11:28 +0100 Subject: Support for named indices for array-like compound literals `{3 = a, 1 = b}` --- src/check_expr.cpp | 103 +++++++++++++++++++++++++++++++++++++----------- src/ir.cpp | 112 +++++++++++++++++++++++++++++++++++++++-------------- src/ir_print.cpp | 60 +++++++++++++++++++++------- src/parser.hpp | 1 + 4 files changed, 213 insertions(+), 63 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b2a2528dd..60c057ae9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7066,7 +7066,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type i64 max = 0; - isize index = 0; Type *bet = base_type(elem_type); if (!elem_type_can_be_constant(bet)) { @@ -7077,40 +7076,100 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type break; } - for (; index < cl->elems.count; index++) { - Ast *e = cl->elems[index]; - if (e == nullptr) { - error(node, "Invalid literal element"); - continue; - } + if (cl->elems[0]->kind == Ast_FieldValue) { + if (is_type_simd_vector(t)) { + error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); + } else { + Map seen = {}; + map_init(&seen, heap_allocator()); + defer (map_destroy(&seen)); - if (e->kind == Ast_FieldValue) { - error(e, "'field = value' is only allowed in struct literals"); - continue; - } + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + + Operand op_index = {}; + check_expr(c, &op_index, fv->field); + + if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { + error(elem, "Expected a constant integer as an array field"); + continue; + } + + i64 index = exact_value_to_i64(op_index.value); + + if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); + continue; + } + + if (map_get(&seen, hash_integer(u64(index))) != nullptr) { + error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); + continue; + } + map_set(&seen, hash_integer(u64(index)), true); + + if (max < index) { + max = index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } - if (0 <= max_type_count && max_type_count <= index) { - error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); + cl->max_index = max; } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, e, elem_type); - check_assignment(c, &operand, elem_type, context_name); - is_constant = is_constant && operand.mode == Addressing_Constant; - } - if (max < index) { - max = index; + } else { + isize index = 0; + for (; index < cl->elems.count; index++) { + Ast *e = cl->elems[index]; + if (e == nullptr) { + error(node, "Invalid literal element"); + continue; + } + + if (e->kind == Ast_FieldValue) { + error(e, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + + if (0 <= max_type_count && max_type_count <= index) { + error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, e, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } + + if (max < index) { + max = index; + } } + if (t->kind == Type_Array) { if (is_to_be_determined_array_count) { t->Array.count = max; - } else if (0 < max && max < t->Array.count) { - error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); + } else if (cl->elems[0]->kind != Ast_FieldValue) { + if (0 < max && max < t->Array.count) { + error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); + } } } + if (t->kind == Type_SimdVector) { if (!is_constant) { error(node, "Expected all constant elements for a simd vector"); diff --git a/src/ir.cpp b/src/ir.cpp index 0bf706f67..bea9ee8b3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1552,6 +1552,7 @@ irValue *ir_add_module_constant(irModule *m, Type *type, ExactValue value) { if (count == 0) { return ir_value_nil(type); } + count = gb_max(cl->max_index+1, count); Type *elem = base_type(type)->Slice.elem; Type *t = alloc_type_array(elem, count); irValue *backing_array = ir_add_module_constant(m, t, value); @@ -7859,13 +7860,29 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { // NOTE(bill): Separate value, gep, store into their own chunks for_array(i, cl->elems) { Ast *elem = cl->elems[i]; - if (ir_is_elem_const(proc->module, elem, et)) { - continue; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + if (ir_is_elem_const(proc->module, fv->value, et)) { + continue; + } + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); + + irCompoundLitElemTempData data = {}; + data.expr = fv->value; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + + } else { + if (ir_is_elem_const(proc->module, elem, et)) { + continue; + } + irCompoundLitElemTempData data = {}; + data.expr = elem; + data.elem_index = cast(i32)i; + array_add(&temp_data, data); } - irCompoundLitElemTempData data = {}; - data.expr = elem; - data.elem_index = cast(i32)i; - array_add(&temp_data, data); } for_array(i, temp_data) { @@ -7881,6 +7898,9 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { defer (proc->return_ptr_hint_used = return_ptr_hint_used); Ast *expr = temp_data[i].expr; + if (expr == nullptr) { + continue; + } proc->return_ptr_hint_value = temp_data[i].gep; proc->return_ptr_hint_ast = unparen_expr(expr); @@ -7918,18 +7938,40 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { for_array(i, cl->elems) { Ast *elem = cl->elems[i]; - if (ir_is_elem_const(proc->module, elem, et)) { - continue; - } - irValue *field_expr = ir_build_expr(proc, elem); - Type *t = ir_type(field_expr); - GB_ASSERT(t->kind != Type_Tuple); - irValue *ev = ir_emit_conv(proc, field_expr, et); + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); - irCompoundLitElemTempData data = {}; - data.value = ev; - data.elem_index = cast(i32)i; - array_add(&temp_data, data); + if (ir_is_elem_const(proc->module, fv->value, et)) { + continue; + } + + + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(fv->field->tav.value); + + irValue *field_expr = ir_build_expr(proc, fv->value); + GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + + irValue *ev = ir_emit_conv(proc, field_expr, et); + + irCompoundLitElemTempData data = {}; + data.value = ev; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } else { + if (ir_is_elem_const(proc->module, elem, et)) { + continue; + } + irValue *field_expr = ir_build_expr(proc, elem); + GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + + irValue *ev = ir_emit_conv(proc, field_expr, et); + + irCompoundLitElemTempData data = {}; + data.value = ev; + data.elem_index = cast(i32)i; + array_add(&temp_data, data); + } } for_array(i, temp_data) { @@ -7950,28 +7992,42 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { if (cl->elems.count == 0) { break; } - Type *elem = bt->DynamicArray.elem; + Type *et = bt->DynamicArray.elem; gbAllocator a = ir_allocator(); - irValue *size = ir_const_int(type_size_of(elem)); - irValue *align = ir_const_int(type_align_of(elem)); + irValue *size = ir_const_int(type_size_of(et)); + irValue *align = ir_const_int(type_align_of(et)); + + i64 item_count = gb_max(cl->max_index+1, cl->elems.count); { + auto args = array_make(a, 5); args[0] = ir_emit_conv(proc, v, t_rawptr); args[1] = size; args[2] = align; - args[3] = ir_const_int(2*cl->elems.count); + args[3] = ir_const_int(2*item_count); // TODO(bill): Is this too much waste? args[4] = ir_emit_source_code_location(proc, proc_name, pos); ir_emit_runtime_call(proc, "__dynamic_array_reserve", args); } - i64 item_count = cl->elems.count; - irValue *items = ir_generate_array(proc->module, elem, item_count, str_lit("dacl$"), cast(i64)cast(intptr)expr); + irValue *items = ir_generate_array(proc->module, et, item_count, str_lit("dacl$"), cast(i64)cast(intptr)expr); - for_array(field_index, cl->elems) { - Ast *f = cl->elems[field_index]; - irValue *value = ir_emit_conv(proc, ir_build_expr(proc, f), elem); - irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); - ir_emit_store(proc, ep, value); + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + + i64 field_index = exact_value_to_i64(fv->field->tav.value); + + irValue *ev = ir_build_expr(proc, fv->value); + irValue *value = ir_emit_conv(proc, ev, et); + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); + ir_emit_store(proc, ep, value); + } else { + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, elem), et); + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)i); + ir_emit_store(proc, ep, value); + } } { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index f76539fd6..cece0c1db 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -876,22 +876,56 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_str_lit(f, "zeroinitializer"); break; } - GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); + if (cl->elems[0]->kind == Ast_FieldValue) { + // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand + ir_write_byte(f, '['); + for (i64 i = 0; i < type->Array.count; i++) { + if (i > 0) ir_write_str_lit(f, ", "); + + bool found = false; + + for (isize j = 0; j < elem_count; j++) { + Ast *elem = cl->elems[j]; + ast_node(fv, FieldValue, elem); + TypeAndValue index_tav = fv->field->tav; + GB_ASSERT(index_tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(index_tav.value); + if (index == i) { + TypeAndValue tav = fv->value->tav; + if (tav.mode != Addressing_Constant) { + break; + } + ir_print_compound_element(f, m, tav.value, elem_type); + found = true; + break; + } + } - ir_write_byte(f, '['); + if (!found) { + ir_print_type(f, m, elem_type); + ir_write_byte(f, ' '); + ir_write_str_lit(f, "zeroinitializer"); + } + } + ir_write_byte(f, ']'); + } else { + GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); - for (isize i = 0; i < elem_count; i++) { - if (i > 0) ir_write_str_lit(f, ", "); - TypeAndValue tav = cl->elems[i]->tav; - GB_ASSERT(tav.mode != Addressing_Invalid); - ir_print_compound_element(f, m, tav.value, elem_type); - } - for (isize i = elem_count; i < type->Array.count; i++) { - if (i >= elem_count) ir_write_str_lit(f, ", "); - ir_print_compound_element(f, m, empty_exact_value, elem_type); - } + ir_write_byte(f, '['); - ir_write_byte(f, ']'); + for (isize i = 0; i < elem_count; i++) { + if (i > 0) ir_write_str_lit(f, ", "); + TypeAndValue tav = cl->elems[i]->tav; + GB_ASSERT(tav.mode != Addressing_Invalid); + ir_print_compound_element(f, m, tav.value, elem_type); + } + for (isize i = elem_count; i < type->Array.count; i++) { + if (i >= elem_count) ir_write_str_lit(f, ", "); + ir_print_compound_element(f, m, empty_exact_value, elem_type); + } + + ir_write_byte(f, ']'); + } } else if (is_type_simd_vector(type)) { ast_node(cl, CompoundLit, value.value_compound); diff --git a/src/parser.hpp b/src/parser.hpp index 419cf9da3..9c3f733e5 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -249,6 +249,7 @@ enum StmtAllowFlag { Ast *type; \ Array elems; \ Token open, close; \ + i64 max_index; \ }) \ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ -- cgit v1.2.3 From e15dfa8eb6c64b9c39fbd8ea0712e31082137d63 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 20 Oct 2019 18:26:30 +0100 Subject: Fix missing check for zero elements --- src/check_expr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 60c057ae9..195443159 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7076,7 +7076,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type break; } - if (cl->elems[0]->kind == Ast_FieldValue) { + if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { if (is_type_simd_vector(t)) { error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); } else { @@ -7162,7 +7162,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (t->kind == Type_Array) { if (is_to_be_determined_array_count) { t->Array.count = max; - } else if (cl->elems[0]->kind != Ast_FieldValue) { + } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { if (0 < max && max < t->Array.count) { error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); } -- cgit v1.2.3 From 2c75fe2314aa6de6c8f80d519bbb1812ad77b444 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 26 Oct 2019 11:50:42 +0100 Subject: Allow for cycles in record polymorphic parameters but not in actualized fields --- src/check_expr.cpp | 72 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 31 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 195443159..ea9248089 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6127,47 +6127,57 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper defer (array_free(&operands)); bool named_fields = false; - - if (is_call_expr_field_value(ce)) { - named_fields = true; - operands = array_make(heap_allocator(), ce->args.count); - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - ast_node(fv, FieldValue, arg); - - if (fv->field->kind == Ast_Ident) { - String name = fv->field->Ident.token.string; - isize index = lookup_polymorphic_record_parameter(original_type, name); - if (index >= 0) { - TypeTuple *params = get_record_polymorphic_params(original_type); - Entity *e = params->variables[i]; - if (e->kind == Entity_Constant) { - check_expr_with_type_hint(c, &operands[i], fv->value, e->type); + { + // NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves + auto prev_type_path = c->type_path; + c->type_path = new_checker_type_path(); + defer ({ + destroy_checker_type_path(c->type_path); + c->type_path = prev_type_path; + }); + + if (is_call_expr_field_value(ce)) { + named_fields = true; + operands = array_make(heap_allocator(), ce->args.count); + for_array(i, ce->args) { + Ast *arg = ce->args[i]; + ast_node(fv, FieldValue, arg); + + if (fv->field->kind == Ast_Ident) { + String name = fv->field->Ident.token.string; + isize index = lookup_polymorphic_record_parameter(original_type, name); + if (index >= 0) { + TypeTuple *params = get_record_polymorphic_params(original_type); + Entity *e = params->variables[i]; + if (e->kind == Entity_Constant) { + check_expr_with_type_hint(c, &operands[i], fv->value, e->type); + } } + } + check_expr_or_type(c, &operands[i], fv->value); + } + bool vari_expand = (ce->ellipsis.pos.line != 0); + if (vari_expand) { + error(ce->ellipsis, "Invalid use of '..' in a polymorphic type call'"); } - check_expr_or_type(c, &operands[i], fv->value); - } - bool vari_expand = (ce->ellipsis.pos.line != 0); - if (vari_expand) { - error(ce->ellipsis, "Invalid use of '..' in a polymorphic type call'"); - } + } else { + operands = array_make(heap_allocator(), 0, 2*ce->args.count); - } else { - operands = array_make(heap_allocator(), 0, 2*ce->args.count); + Entity **lhs = nullptr; + isize lhs_count = -1; - Entity **lhs = nullptr; - isize lhs_count = -1; + TypeTuple *params = get_record_polymorphic_params(original_type); + if (params != nullptr) { + lhs = params->variables.data; + lhs_count = params->variables.count; + } - TypeTuple *params = get_record_polymorphic_params(original_type); - if (params != nullptr) { - lhs = params->variables.data; - lhs_count = params->variables.count; + check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, false); } - check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, false); } CallArgumentError err = CallArgumentError_None; -- cgit v1.2.3 From 7fae890ef9eb72f3131914f9af2469f9bfb1e59e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 26 Oct 2019 14:06:29 +0100 Subject: Allow ranges for array-like compound literals --- core/odin/parser/parser.odin | 3 + src/check_expr.cpp | 229 ++++++++++++++++++++++++++++++++++--------- src/check_stmt.cpp | 84 +--------------- src/common.cpp | 3 + src/exact_value.cpp | 2 +- src/ir.cpp | 124 ++++++++++++++++++----- src/ir_print.cpp | 48 +++++++-- src/parser.cpp | 11 ++- src/parser.hpp | 1 + 9 files changed, 340 insertions(+), 165 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index fee9b7bdc..fb3a87e8b 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2391,6 +2391,9 @@ parse_value :: proc(p: ^Parser) -> ^ast.Expr { if p.curr_tok.kind == .Open_Brace { return parse_literal_value(p, nil); } + prev_allow_range = p.allow_range; + defer p.allow_range = prev_allow_range; + p.allow_range = true; return parse_expr(p, false); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ea9248089..f07ebe09c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6635,6 +6635,98 @@ bool ternary_compare_types(Type *x, Type *y) { } +bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValue *inline_for_depth_) { + if (!is_ast_range(node)) { + return false; + } + + ast_node(ie, BinaryExpr, node); + + check_expr(c, x, ie->left); + if (x->mode == Addressing_Invalid) { + return false; + } + check_expr(c, y, ie->right); + if (y->mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, x, y->type); + if (x->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, y, x->type); + if (y->mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, x, default_type(y->type)); + if (x->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, y, default_type(x->type)); + if (y->mode == Addressing_Invalid) { + return false; + } + + if (!are_types_identical(x->type, y->type)) { + if (x->type != t_invalid && + y->type != t_invalid) { + gbString xt = type_to_string(x->type); + gbString yt = type_to_string(y->type); + gbString expr_str = expr_to_string(x->expr); + error(ie->op, "Mismatched types in interval expression '%s' : '%s' vs '%s'", expr_str, xt, yt); + gb_string_free(expr_str); + gb_string_free(yt); + gb_string_free(xt); + } + return false; + } + + Type *type = x->type; + if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { + error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); + return false; + } + + if (x->mode == Addressing_Constant && + y->mode == Addressing_Constant) { + ExactValue a = x->value; + ExactValue b = y->value; + + GB_ASSERT(are_types_identical(x->type, y->type)); + + TokenKind op = Token_Lt; + switch (ie->op.kind) { + case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeHalf: op = Token_Lt; break; + default: error(ie->op, "Invalid range operator"); break; + } + bool ok = compare_exact_values(op, a, b); + if (!ok) { + // TODO(bill): Better error message + error(ie->op, "Invalid interval range"); + return false; + } + + ExactValue inline_for_depth = exact_value_sub(b, a); + if (ie->op.kind == Token_Ellipsis) { + inline_for_depth = exact_value_increment_one(inline_for_depth); + } + + if (inline_for_depth_) *inline_for_depth_ = inline_for_depth; + } else { + error(ie->op, "Interval expressions must be constant"); + return false; + } + + add_type_and_value(&c->checker->info, ie->left, x->mode, x->type, x->value); + add_type_and_value(&c->checker->info, ie->right, y->mode, y->type, y->value); + + return true; +} + + ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Stmt; @@ -6697,35 +6789,26 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(bl, BasicLit, node); - // NOTE(bill, 2018-06-17): Placing this in the parser is slower than - // placing it here for some reason. So don't move it to the parsing - // stage if you _think_ it will be faster, only do it if you _know_ it - // will be faster. Type *t = t_invalid; - switch (bl->token.kind) { - case Token_Integer: t = t_untyped_integer; break; - case Token_Float: t = t_untyped_float; break; - case Token_String: t = t_untyped_string; break; - case Token_Rune: t = t_untyped_rune; break; - case Token_Imag: { - String s = bl->token.string; - Rune r = s[s.len-1]; - // NOTE(bill, 2019-08-25): Allow for quaternions by having j and k imaginary numbers - switch (r) { - case 'i': t = t_untyped_complex; break; - case 'j': t = t_untyped_quaternion; break; - case 'k': t = t_untyped_quaternion; break; + switch (bl->value.kind) { + case ExactValue_String: t = t_untyped_string; break; + case ExactValue_Float: t = t_untyped_float; break; + case ExactValue_Complex: t = t_untyped_complex; break; + case ExactValue_Quaternion: t = t_untyped_quaternion; break; + case ExactValue_Integer: + t = t_untyped_integer; + if (bl->token.kind == Token_Rune) { + t = t_untyped_rune; } - break; - } default: - GB_PANIC("Unknown literal"); + GB_PANIC("Unhandled value type for basic literal"); break; } + o->mode = Addressing_Constant; o->type = t; - o->value = exact_value_from_basic_literal(bl->token); + o->value = bl->value; case_end; case_ast_node(bd, BasicDirective, node); @@ -7090,9 +7173,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (is_type_simd_vector(t)) { error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); } else { - Map seen = {}; - map_init(&seen, heap_allocator()); - defer (map_destroy(&seen)); + RangeCache rc = range_cache_make(heap_allocator()); + defer (range_cache_destroy(&rc)); for_array(i, cl->elems) { Ast *elem = cl->elems[i]; @@ -7102,36 +7184,89 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } ast_node(fv, FieldValue, elem); - Operand op_index = {}; - check_expr(c, &op_index, fv->field); + if (is_ast_range(fv->field)) { + Token op = fv->field->BinaryExpr.op; - if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { - error(elem, "Expected a constant integer as an array field"); - continue; - } + Operand x = {}; + Operand y = {}; + bool ok = check_range(c, fv->field, &x, &y, nullptr); + if (!ok) { + continue; + } + if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) { + error(x.expr, "Expected a constant integer as an array field"); + continue; + } - i64 index = exact_value_to_i64(op_index.value); + if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) { + error(y.expr, "Expected a constant integer as an array field"); + continue; + } - if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); - continue; - } + i64 lo = exact_value_to_i64(x.value); + i64 hi = exact_value_to_i64(y.value); + if (op.kind == Token_RangeHalf) { + hi -= 1; + } + i64 max_index = hi; - if (map_get(&seen, hash_integer(u64(index))) != nullptr) { - error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); - continue; - } - map_set(&seen, hash_integer(u64(index)), true); + bool new_range = range_cache_add_range(&rc, lo, hi); + if (!new_range) { + error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name)); + continue; + } - if (max < index) { - max = index; - } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); + if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name)); + continue; + } + if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name)); + continue; + } + + if (max < max_index) { + max = max_index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } else { + Operand op_index = {}; + check_expr(c, &op_index, fv->field); + + if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { + error(elem, "Expected a constant integer as an array field"); + continue; + } - is_constant = is_constant && operand.mode == Addressing_Constant; + i64 index = exact_value_to_i64(op_index.value); + + if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); + continue; + } + + bool new_index = range_cache_add_index(&rc, index); + if (!new_index) { + error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); + continue; + } + + if (max < index) { + max = index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } } cl->max_index = max; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 72913903d..d4398664b 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -605,89 +605,15 @@ void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { if (is_ast_range(expr)) { ast_node(ie, BinaryExpr, expr); - Operand x = {Addressing_Invalid}; - Operand y = {Addressing_Invalid}; + Operand x = {}; + Operand y = {}; - check_expr(ctx, &x, ie->left); - if (x.mode == Addressing_Invalid) { - goto skip_expr; - } - check_expr(ctx, &y, ie->right); - if (y.mode == Addressing_Invalid) { - goto skip_expr; - } - - convert_to_typed(ctx, &x, y.type); - if (x.mode == Addressing_Invalid) { - goto skip_expr; - } - convert_to_typed(ctx, &y, x.type); - if (y.mode == Addressing_Invalid) { - goto skip_expr; - } - - convert_to_typed(ctx, &x, default_type(y.type)); - if (x.mode == Addressing_Invalid) { - goto skip_expr; - } - convert_to_typed(ctx, &y, default_type(x.type)); - if (y.mode == Addressing_Invalid) { - goto skip_expr; - } - - if (!are_types_identical(x.type, y.type)) { - if (x.type != t_invalid && - y.type != t_invalid) { - gbString xt = type_to_string(x.type); - gbString yt = type_to_string(y.type); - gbString expr_str = expr_to_string(x.expr); - error(ie->op, "Mismatched types in interval expression '%s' : '%s' vs '%s'", expr_str, xt, yt); - gb_string_free(expr_str); - gb_string_free(yt); - gb_string_free(xt); - } - goto skip_expr; - } - - Type *type = x.type; - if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { - error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); - goto skip_expr; - } - - if (x.mode == Addressing_Constant && - y.mode == Addressing_Constant) { - ExactValue a = x.value; - ExactValue b = y.value; - - GB_ASSERT(are_types_identical(x.type, y.type)); - - TokenKind op = Token_Lt; - switch (ie->op.kind) { - case Token_Ellipsis: op = Token_LtEq; break; - case Token_RangeHalf: op = Token_Lt; break; - default: error(ie->op, "Invalid range operator"); break; - } - bool ok = compare_exact_values(op, a, b); - if (!ok) { - // TODO(bill): Better error message - error(ie->op, "Invalid interval range"); - goto skip_expr; - } - - inline_for_depth = exact_value_sub(b, a); - if (ie->op.kind == Token_Ellipsis) { - inline_for_depth = exact_value_increment_one(inline_for_depth); - } - - } else { - error(ie->op, "Interval expressions must be constant"); + bool ok = check_range(ctx, expr, &x, &y, &inline_for_depth); + if (!ok) { goto skip_expr; } - add_type_and_value(&ctx->checker->info, ie->left, x.mode, x.type, x.value); - add_type_and_value(&ctx->checker->info, ie->right, y.mode, y.type, y.value); - val0 = type; + val0 = x.type; val1 = t_int; } else { Operand operand = {Addressing_Invalid}; diff --git a/src/common.cpp b/src/common.cpp index db6505a36..b034ad720 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -143,6 +143,9 @@ GB_ALLOCATOR_PROC(heap_allocator_proc) { #define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++) +#include "range_cache.cpp" + + u64 fnv64a(void const *data, isize len) { u8 const *bytes = cast(u8 const *)data; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 95d04d93b..42b22c8ef 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -293,12 +293,12 @@ ExactValue exact_value_from_basic_literal(Token token) { case 'i': return exact_value_complex(0, imag); case 'j': return exact_value_quaternion(0, 0, imag, 0); case 'k': return exact_value_quaternion(0, 0, 0, imag); + default: GB_PANIC("Invalid imaginary basic literal"); } } case Token_Rune: { Rune r = GB_RUNE_INVALID; gb_utf8_decode(token.string.text, token.string.len, &r); - // gb_printf("%.*s rune: %d\n", LIT(token.string), r); return exact_value_i64(r); } default: diff --git a/src/ir.cpp b/src/ir.cpp index c654e2bf8..5eeba91a2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7865,14 +7865,40 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { if (ir_is_elem_const(proc->module, fv->value, et)) { continue; } - auto tav = fv->field->tav; - GB_ASSERT(tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(tav.value); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - irCompoundLitElemTempData data = {}; - data.expr = fv->value; - data.elem_index = cast(i32)index; - array_add(&temp_data, data); + irValue *value = ir_build_expr(proc, fv->value); + + for (i64 k = lo; k < hi; k++) { + irCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } + + } else { + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); + + irCompoundLitElemTempData data = {}; + data.value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); + data.expr = fv->value; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } } else { if (ir_is_elem_const(proc->module, elem, et)) { @@ -7897,15 +7923,15 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { defer (proc->return_ptr_hint_value = return_ptr_hint_value); defer (proc->return_ptr_hint_used = return_ptr_hint_used); + irValue *field_expr = temp_data[i].value; Ast *expr = temp_data[i].expr; - if (expr == nullptr) { - continue; - } proc->return_ptr_hint_value = temp_data[i].gep; proc->return_ptr_hint_ast = unparen_expr(expr); - irValue *field_expr = ir_build_expr(proc, expr); + if (field_expr == nullptr) { + field_expr = ir_build_expr(proc, expr); + } Type *t = ir_type(field_expr); GB_ASSERT(t->kind != Type_Tuple); irValue *ev = ir_emit_conv(proc, field_expr, et); @@ -7945,19 +7971,43 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { continue; } + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - GB_ASSERT(fv->field->tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(fv->field->tav.value); + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); - irValue *field_expr = ir_build_expr(proc, fv->value); - GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + for (i64 k = lo; k < hi; k++) { + irCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } - irValue *ev = ir_emit_conv(proc, field_expr, et); + } else { + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(fv->field->tav.value); - irCompoundLitElemTempData data = {}; - data.value = ev; - data.elem_index = cast(i32)index; - array_add(&temp_data, data); + irValue *field_expr = ir_build_expr(proc, fv->value); + GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + + irValue *ev = ir_emit_conv(proc, field_expr, et); + + irCompoundLitElemTempData data = {}; + data.value = ev; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } } else { if (ir_is_elem_const(proc->module, elem, et)) { continue; @@ -8015,14 +8065,36 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { Ast *elem = cl->elems[i]; if (elem->kind == Ast_FieldValue) { ast_node(fv, FieldValue, elem); - GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - i64 field_index = exact_value_to_i64(fv->field->tav.value); + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); - irValue *ev = ir_build_expr(proc, fv->value); - irValue *value = ir_emit_conv(proc, ev, et); - irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); - ir_emit_store(proc, ep, value); + for (i64 k = lo; k < hi; k++) { + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)k); + ir_emit_store(proc, ep, value); + } + } else { + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + + i64 field_index = exact_value_to_i64(fv->field->tav.value); + + irValue *ev = ir_build_expr(proc, fv->value); + irValue *value = ir_emit_conv(proc, ev, et); + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); + ir_emit_store(proc, ep, value); + } } else { irValue *value = ir_emit_conv(proc, ir_build_expr(proc, elem), et); irValue *ep = ir_emit_array_epi(proc, items, cast(i32)i); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index cece0c1db..1d0c8af35 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -887,17 +887,47 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * for (isize j = 0; j < elem_count; j++) { Ast *elem = cl->elems[j]; ast_node(fv, FieldValue, elem); - TypeAndValue index_tav = fv->field->tav; - GB_ASSERT(index_tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(index_tav.value); - if (index == i) { - TypeAndValue tav = fv->value->tav; - if (tav.mode != Addressing_Constant) { + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } + if (lo == i) { + TypeAndValue tav = fv->value->tav; + if (tav.mode != Addressing_Constant) { + break; + } + for (i64 k = lo; k < hi; k++) { + if (k > lo) ir_write_str_lit(f, ", "); + + ir_print_compound_element(f, m, tav.value, elem_type); + } + + found = true; + i += (hi-lo-1); + break; + } + } else { + TypeAndValue index_tav = fv->field->tav; + GB_ASSERT(index_tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(index_tav.value); + if (index == i) { + TypeAndValue tav = fv->value->tav; + if (tav.mode != Addressing_Constant) { + break; + } + ir_print_compound_element(f, m, tav.value, elem_type); + found = true; break; } - ir_print_compound_element(f, m, tav.value, elem_type); - found = true; - break; } } diff --git a/src/parser.cpp b/src/parser.cpp index 7f866922a..10aa10119 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -588,6 +588,7 @@ Ast *ast_undef(AstFile *f, Token token) { Ast *ast_basic_lit(AstFile *f, Token basic_lit) { Ast *result = alloc_ast_node(f, Ast_BasicLit); result->BasicLit.token = basic_lit; + result->BasicLit.value = exact_value_from_basic_literal(basic_lit); return result; } @@ -1509,8 +1510,11 @@ Ast *parse_value(AstFile *f) { if (f->curr_token.kind == Token_OpenBrace) { return parse_literal_value(f, nullptr); } - - Ast *value = parse_expr(f, false); + Ast *value; + bool prev_allow_range = f->allow_range; + f->allow_range = true; + value = parse_expr(f, false); + f->allow_range = prev_allow_range; return value; } @@ -1735,7 +1739,8 @@ Ast *parse_operand(AstFile *f, bool lhs) { operand = ast_bad_expr(f, token, f->curr_token); } operand->stmt_state_flags |= StmtStateFlag_no_deferred; - } */ else if (name.string == "file") { return ast_basic_directive(f, token, name.string); + } */ else if (name.string == "file") { + return ast_basic_directive(f, token, name.string); } else if (name.string == "line") { return ast_basic_directive(f, token, name.string); } else if (name.string == "procedure") { return ast_basic_directive(f, token, name.string); } else if (name.string == "caller_location") { return ast_basic_directive(f, token, name.string); diff --git a/src/parser.hpp b/src/parser.hpp index 9c3f733e5..83df7a9d6 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -222,6 +222,7 @@ enum StmtAllowFlag { AST_KIND(Undef, "undef", Token) \ AST_KIND(BasicLit, "basic literal", struct { \ Token token; \ + ExactValue value; \ }) \ AST_KIND(BasicDirective, "basic directive", struct { \ Token token; \ -- cgit v1.2.3 From a5e42a046531405e0b8830ae092470c2aa2e67a8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 26 Oct 2019 14:36:28 +0100 Subject: Add `ranged_fields_for_array_compound_literals` --- examples/demo/demo.odin | 44 +++++++++++++++++++++++++++++++++++++------- src/check_expr.cpp | 9 ++++++--- 2 files changed, 43 insertions(+), 10 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index cd6ac6bf5..fa970fa43 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1194,13 +1194,43 @@ ranged_fields_for_array_compound_literals :: proc() { foo := [?]int{1, 4, 9, 16}; fmt.println(foo); } - i := 2; - foo := [?]int { - 0 = 123, - 5..9 = 54, - 10..<16 = i*3 + (i-1)*2, - }; - fmt.println(foo); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8] + { // Indexed + foo := [?]int{ + 3 = 16, + 1 = 4, + 2 = 9, + 0 = 1, + }; + fmt.println(foo); + } + { // Ranges + i := 2; + foo := [?]int { + 0 = 123, + 5..9 = 54, + 10..<16 = i*3 + (i-1)*2, + }; + #assert(len(foo) == 16); + fmt.println(foo); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8] + } + { // Slice and Dynamic Array support + i := 2; + foo_slice := []int { + 0 = 123, + 5..9 = 54, + 10..<16 = i*3 + (i-1)*2, + }; + assert(len(foo) == 16); + fmt.println(foo_slice); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8] + + foo_dynamic_array := [dynamic]int { + 0 = 123, + 5..9 = 54, + 10..<16 = i*3 + (i-1)*2, + }; + assert(len(foo) == 16); + fmt.println(foo_dynamic_array); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8] + } } main :: proc() { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f07ebe09c..7491094c8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7205,10 +7205,13 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type i64 lo = exact_value_to_i64(x.value); i64 hi = exact_value_to_i64(y.value); + i64 max_index = hi; if (op.kind == Token_RangeHalf) { hi -= 1; } - i64 max_index = hi; + if (op.kind == Token_Ellipsis) { + max_index += 1; + } bool new_range = range_cache_add_range(&rc, lo, hi); if (!new_range) { @@ -7257,8 +7260,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type continue; } - if (max < index) { - max = index; + if (max < index+1) { + max = index+1; } Operand operand = {}; -- cgit v1.2.3 From 14e8b299b73c87d5c48e73add91d7a427d554d75 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 26 Oct 2019 14:43:06 +0100 Subject: Fix slice and dynamic array lengths determined from ranged compound literals --- src/check_expr.cpp | 7 ++----- src/ir.cpp | 4 ++-- src/parser.hpp | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7491094c8..3c4d737a4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7209,9 +7209,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (op.kind == Token_RangeHalf) { hi -= 1; } - if (op.kind == Token_Ellipsis) { - max_index += 1; - } bool new_range = range_cache_add_range(&rc, lo, hi); if (!new_range) { @@ -7229,7 +7226,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type continue; } - if (max < max_index) { + if (max < hi) { max = max_index; } @@ -7272,7 +7269,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } } - cl->max_index = max; + cl->max_count = max; } diff --git a/src/ir.cpp b/src/ir.cpp index 5eeba91a2..ae60be7e3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1552,7 +1552,7 @@ irValue *ir_add_module_constant(irModule *m, Type *type, ExactValue value) { if (count == 0) { return ir_value_nil(type); } - count = gb_max(cl->max_index+1, count); + count = gb_max(cl->max_count, count); Type *elem = base_type(type)->Slice.elem; Type *t = alloc_type_array(elem, count); irValue *backing_array = ir_add_module_constant(m, t, value); @@ -8047,7 +8047,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { irValue *size = ir_const_int(type_size_of(et)); irValue *align = ir_const_int(type_align_of(et)); - i64 item_count = gb_max(cl->max_index+1, cl->elems.count); + i64 item_count = gb_max(cl->max_count, cl->elems.count); { auto args = array_make(a, 5); diff --git a/src/parser.hpp b/src/parser.hpp index 83df7a9d6..f07f3ce0d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -250,7 +250,7 @@ enum StmtAllowFlag { Ast *type; \ Array elems; \ Token open, close; \ - i64 max_index; \ + i64 max_count; \ }) \ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ -- cgit v1.2.3 From 5e81fc72b97dc3a27fee3ea1e0ae9bffab56e8d5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 27 Oct 2019 10:35:35 +0000 Subject: New `package math` and `package math/linalg` --- core/math/linalg/linalg.odin | 257 +++++++++++++++ core/math/math.odin | 725 +++++++++++++++++++++---------------------- src/check_expr.cpp | 28 +- 3 files changed, 643 insertions(+), 367 deletions(-) create mode 100644 core/math/linalg/linalg.odin (limited to 'src/check_expr.cpp') diff --git a/core/math/linalg/linalg.odin b/core/math/linalg/linalg.odin new file mode 100644 index 000000000..9a4781668 --- /dev/null +++ b/core/math/linalg/linalg.odin @@ -0,0 +1,257 @@ +package linalg + +import "core:math" + +// Generic + +dot_vector :: proc(a, b: $T/[$N]$E) -> (c: E) { + for i in 0.. (c: f32) { + return real(a)*real(a) + imag(a)*imag(b) + jmag(a)*jmag(b) + kmag(a)*kmag(b); +} +dot_quaternion256 :: proc(a, b: $T/quaternion256) -> (c: f64) { + return real(a)*real(a) + imag(a)*imag(b) + jmag(a)*jmag(b) + kmag(a)*kmag(b); +} + +dot :: proc{dot_vector, dot_quaternion128, dot_quaternion256}; + +cross2 :: proc(a, b: $T/[2]$E) -> E { + return a[0]*b[1] - b[0]*a[1]; +} + +cross3 :: proc(a, b: $T/[3]$E) -> (c: T) { + c[0] = +(a[1]*b[2] - b[1]*a[2]); + c[1] = -(a[2]*b[3] - b[2]*a[3]); + c[2] = +(a[3]*b[1] - b[3]*a[1]); + return; +} + +cross :: proc{cross2, cross3}; + + +normalize_vector :: proc(v: $T/[$N]$E) -> T { + return v / length(v); +} +normalize_quaternion128 :: proc(q: $Q/quaternion128) -> Q { + return q/abs(q); +} +normalize_quaternion256 :: proc(q: $Q/quaternion256) -> Q { + return q/abs(q); +} +normalize :: proc{normalize_vector, normalize_quaternion128, normalize_quaternion256}; + +normalize0_vector :: proc(v: $T/[$N]$E) -> T { + m := length(v); + return m == 0 ? 0 : v/m; +} +normalize0_quaternion128 :: proc(q: $Q/quaternion128) -> Q { + m := abs(q); + return m == 0 ? 0 : q/m; +} +normalize0_quaternion256 :: proc(q: $Q/quaternion256) -> Q { + m := abs(q); + return m == 0 ? 0 : q/m; +} +normalize0 :: proc{normalize0_vector, normalize0_quaternion128, normalize0_quaternion256}; + + +length :: proc(v: $T/[$N]$E) -> E { + return math.sqrt(dot(v, v)); +} + + +identity :: proc($T: typeid/[$N][N]$E) -> (m: T) { + for i in 0.. (m: ((M == N) ? T : [M][N]E)) { + for j in 0.. (c: ((I == J && J == K && A == B) ? A : [I][K]E)) { + for i in 0.. (c: B) { + for i in 0.. V { + Raw_Quaternion :: struct {xyz: [3]f32, r: f32}; + + q := transmute(Raw_Quaternion)q; + v := transmute([3]f32)v; + + t := cross(2*q.xyz, v); + return V(v + q.r*t + cross(q.xyz, t)); +} + +mul_quaternion256_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V { + Raw_Quaternion :: struct {xyz: [3]f64, r: f64}; + + q := transmute(Raw_Quaternion)q; + v := transmute([3]f64)v; + + t := cross(2*q.xyz, v); + return V(v + q.r*t + cross(q.xyz, t)); +} +mul_quaternion_vector3 :: proc{mul_quaternion128_vector3, mul_quaternion256_vector3}; + +mul :: proc{mul_matrix, mul_matrix_vector, mul_quaternion128_vector3, mul_quaternion256_vector3}; + + +// Specific + +Float :: f32; + +Vector2 :: distinct [2]Float; +Vector3 :: distinct [3]Float; +Vector4 :: distinct [4]Float; + +Matrix2x1 :: distinct [2][1]Float; +Matrix2x2 :: distinct [2][2]Float; +Matrix2x3 :: distinct [2][3]Float; +Matrix2x4 :: distinct [2][4]Float; + +Matrix3x1 :: distinct [3][1]Float; +Matrix3x2 :: distinct [3][2]Float; +Matrix3x3 :: distinct [3][3]Float; +Matrix3x4 :: distinct [3][4]Float; + +Matrix4x1 :: distinct [4][1]Float; +Matrix4x2 :: distinct [4][2]Float; +Matrix4x3 :: distinct [4][3]Float; +Matrix4x4 :: distinct [4][4]Float; + + +Matrix2 :: Matrix2x2; +Matrix3 :: Matrix3x3; +Matrix4 :: Matrix4x4; + + +Quaternion :: distinct (size_of(Float) == size_of(f32) ? quaternion128 : quaternion256); + + +translate_matrix4 :: proc(v: Vector3) -> Matrix4 { + m := identity(Matrix4); + m[3][0] = v[0]; + m[3][1] = v[1]; + m[3][2] = v[2]; + return m; +} + + +rotate_matrix4 :: proc(v: Vector3, angle_radians: Float) -> Matrix4 { + c := math.cos(angle_radians); + s := math.sin(angle_radians); + + a := normalize(v); + t := a * (1-c); + + rot := identity(Matrix4); + + rot[0][0] = c + t[0]*a[0]; + rot[0][1] = 0 + t[0]*a[1] + s*a[2]; + rot[0][2] = 0 + t[0]*a[2] - s*a[1]; + rot[0][3] = 0; + + rot[1][0] = 0 + t[1]*a[0] - s*a[2]; + rot[1][1] = c + t[1]*a[1]; + rot[1][2] = 0 + t[1]*a[2] + s*a[0]; + rot[1][3] = 0; + + rot[2][0] = 0 + t[2]*a[0] + s*a[1]; + rot[2][1] = 0 + t[2]*a[1] - s*a[0]; + rot[2][2] = c + t[2]*a[2]; + rot[2][3] = 0; + + return rot; +} + +scale_matrix4 :: proc(m: Matrix4, v: Vector3) -> Matrix4 { + mm := m; + mm[0][0] *= v[0]; + mm[1][1] *= v[1]; + mm[2][2] *= v[2]; + return mm; +} + + +look_at :: proc(eye, centre, up: Vector3) -> Matrix4 { + f := normalize(centre - eye); + s := normalize(cross(f, up)); + u := cross(s, f); + return Matrix4{ + {+s.x, +u.x, -f.x, 0}, + {+s.y, +u.y, -f.y, 0}, + {+s.z, +u.z, -f.z, 0}, + {-dot(s, eye), -dot(u, eye), +dot(f, eye), 1}, + }; +} + + +perspective :: proc(fovy, aspect, near, far: Float) -> (m: Matrix4) { + tan_half_fovy := math.tan(0.5 * fovy); + m[0][0] = 1 / (aspect*tan_half_fovy); + m[1][1] = 1 / (tan_half_fovy); + m[2][2] = -(far + near) / (far - near); + m[2][3] = -1; + m[3][2] = -2*far*near / (far - near); + return; +} + + +ortho3d :: proc(left, right, bottom, top, near, far: Float) -> (m: Matrix4) { + m[0][0] = +2 / (right - left); + m[1][1] = +2 / (top - bottom); + m[2][2] = -2 / (far - near); + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + m[3][2] = -(far + near) / (far- near); + m[3][3] = 1; + return; +} + + +axis_angle :: proc(axis: Vector3, angle_radians: Float) -> Quaternion { + t := angle_radians*0.5; + w := math.cos(t); + v := normalize(axis) * math.sin(t); + return quaternion(w, v.x, v.y, v.z); +} + +angle_axis :: proc(angle_radians: Float, axis: Vector3) -> Quaternion { + t := angle_radians*0.5; + w := math.cos(t); + v := normalize(axis) * math.sin(t); + return quaternion(w, v.x, v.y, v.z); +} + +euler_angles :: proc(pitch, yaw, roll: Float) -> Quaternion { + p := axis_angle({1, 0, 0}, pitch); + y := axis_angle({0, 1, 0}, yaw); + r := axis_angle({0, 0, 1}, roll); + return (y * p) * r; +} diff --git a/core/math/math.odin b/core/math/math.odin index ed04cbfbc..583da00d0 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -1,36 +1,41 @@ package math +import "intrinsics" + +Float_Class :: enum { + Normal, // an ordinary nonzero floating point value + Subnormal, // a subnormal floating point value + Zero, // zero + Neg_Zero, // the negative zero + NaN, // Not-A-Number (NaN) + Inf, // positive infinity + Neg_Inf // negative infinity +}; + TAU :: 6.28318530717958647692528676655900576; PI :: 3.14159265358979323846264338327950288; E :: 2.71828182845904523536; -SQRT_TWO :: 1.41421356237309504880168872420969808; -SQRT_THREE :: 1.73205080756887729352744634150587236; -SQRT_FIVE :: 2.23606797749978969640917366873127623; - -LOG_TWO :: 0.693147180559945309417232121458176568; -LOG_TEN :: 2.30258509299404568401799145468436421; - -EPSILON :: 1.19209290e-7; τ :: TAU; π :: PI; +e :: E; -Vec2 :: distinct [2]f32; -Vec3 :: distinct [3]f32; -Vec4 :: distinct [4]f32; +SQRT_TWO :: 1.41421356237309504880168872420969808; +SQRT_THREE :: 1.73205080756887729352744634150587236; +SQRT_FIVE :: 2.23606797749978969640917366873127623; -// Column major -Mat2 :: distinct [2][2]f32; -Mat3 :: distinct [3][3]f32; -Mat4 :: distinct [4][4]f32; +LN2 :: 0.693147180559945309417232121458176568; +LN10 :: 2.30258509299404568401799145468436421; -Quat :: struct {x, y, z, w: f32}; +MAX_F64_PRECISION :: 16; // Maximum number of meaningful digits after the decimal point for 'f64' +MAX_F32_PRECISION :: 8; // Maximum number of meaningful digits after the decimal point for 'f32' -QUAT_IDENTITY := Quat{x = 0, y = 0, z = 0, w = 1}; +RAD_PER_DEG :: TAU/360.0; +DEG_PER_RAD :: 360.0/TAU; -@(default_calling_convention="c") +@(default_calling_convention="none") foreign _ { @(link_name="llvm.sqrt.f32") sqrt_f32 :: proc(x: f32) -> f32 ---; @@ -58,9 +63,9 @@ foreign _ { fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---; @(link_name="llvm.log.f32") - log_f32 :: proc(x: f32) -> f32 ---; + ln_f32 :: proc(x: f32) -> f32 ---; @(link_name="llvm.log.f64") - log_f64 :: proc(x: f64) -> f64 ---; + ln_f64 :: proc(x: f64) -> f64 ---; @(link_name="llvm.exp.f32") exp_f32 :: proc(x: f32) -> f32 ---; @@ -68,20 +73,40 @@ foreign _ { exp_f64 :: proc(x: f64) -> f64 ---; } -log :: proc{log_f32, log_f64}; -exp :: proc{exp_f32, exp_f64}; +sqrt :: proc{sqrt_f32, sqrt_f64}; +sin :: proc{sin_f32, sin_f64}; +cos :: proc{cos_f32, cos_f64}; +pow :: proc{pow_f32, pow_f64}; +fmuladd :: proc{fmuladd_f32, fmuladd_f64}; +ln :: proc{ln_f32, ln_f64}; +exp :: proc{exp_f32, exp_f64}; + +log_f32 :: proc(x, base: f32) -> f32 { return ln(x) / ln(base); } +log_f64 :: proc(x, base: f64) -> f64 { return ln(x) / ln(base); } +log :: proc{log_f32, log_f64}; + +log2_f32 :: proc(x: f32) -> f32 { return ln(x)/LN2; } +log2_f64 :: proc(x: f64) -> f64 { return ln(x)/LN2; } +log2 :: proc{log2_f32, log2_f64}; + +log10_f32 :: proc(x: f32) -> f32 { return ln(x)/LN10; } +log10_f64 :: proc(x: f64) -> f64 { return ln(x)/LN10; } +log10 :: proc{log10_f32, log10_f64}; + tan_f32 :: proc "c" (θ: f32) -> f32 { return sin(θ)/cos(θ); } tan_f64 :: proc "c" (θ: f64) -> f64 { return sin(θ)/cos(θ); } +tan :: proc{tan_f32, tan_f64}; lerp :: proc(a, b: $T, t: $E) -> (x: T) { return a*(1-t) + b*t; } unlerp_f32 :: proc(a, b, x: f32) -> (t: f32) { return (x-a)/(b-a); } unlerp_f64 :: proc(a, b, x: f64) -> (t: f64) { return (x-a)/(b-a); } +unlerp :: proc{unlerp_f32, unlerp_f64}; - -sign_f32 :: proc(x: f32) -> f32 { return x >= 0 ? +1 : -1; } -sign_f64 :: proc(x: f64) -> f64 { return x >= 0 ? +1 : -1; } +sign_f32 :: proc(x: f32) -> f32 { return f32(int(0 < x) - int(x < 0)); } +sign_f64 :: proc(x: f64) -> f64 { return f64(int(0 < x) - int(x < 0)); } +sign :: proc{sign_f32, sign_f64}; copy_sign_f32 :: proc(x, y: f32) -> f32 { ix := transmute(u32)x; @@ -90,7 +115,6 @@ copy_sign_f32 :: proc(x, y: f32) -> f32 { ix |= iy & 0x8000_0000; return transmute(f32)ix; } - copy_sign_f64 :: proc(x, y: f64) -> f64 { ix := transmute(u64)x; iy := transmute(u64)y; @@ -98,22 +122,89 @@ copy_sign_f64 :: proc(x, y: f64) -> f64 { ix |= iy & 0x8000_0000_0000_0000; return transmute(f64)ix; } +copy_sign :: proc{copy_sign_f32, copy_sign_f64}; -sqrt :: proc{sqrt_f32, sqrt_f64}; -sin :: proc{sin_f32, sin_f64}; -cos :: proc{cos_f32, cos_f64}; -tan :: proc{tan_f32, tan_f64}; -pow :: proc{pow_f32, pow_f64}; -fmuladd :: proc{fmuladd_f32, fmuladd_f64}; -sign :: proc{sign_f32, sign_f64}; -copy_sign :: proc{copy_sign_f32, copy_sign_f64}; +to_radians_f32 :: proc(degrees: f32) -> f32 { return degrees * RAD_PER_DEG; } +to_radians_f64 :: proc(degrees: f64) -> f64 { return degrees * RAD_PER_DEG; } +to_degrees_f32 :: proc(radians: f32) -> f32 { return radians * DEG_PER_RAD; } +to_degrees_f64 :: proc(radians: f64) -> f64 { return radians * DEG_PER_RAD; } +to_radians :: proc{to_radians_f32, to_radians_f64}; +to_degrees :: proc{to_degrees_f32, to_degrees_f64}; + +trunc_f32 :: proc(x: f32) -> f32 { + trunc_internal :: proc(f: f32) -> f32 { + mask :: 0xff; + shift :: 32 - 9; + bias :: 0x7f; + + if f < 1 { + switch { + case f < 0: return -trunc_internal(-f); + case f == 0: return f; + case: return 0; + } + } + x := transmute(u32)f; + e := (x >> shift) & mask - bias; -round_f32 :: proc(x: f32) -> f32 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); } -round_f64 :: proc(x: f64) -> f64 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); } + if e < shift { + x &= ~(1 << (shift-e)) - 1; + } + return transmute(f32)x; + } + switch classify(x) { + case .Zero, .Neg_Zero, .NaN, .Inf, .Neg_Inf: + return x; + } + return trunc_internal(x); +} + +trunc_f64 :: proc(x: f64) -> f64 { + trunc_internal :: proc(f: f64) -> f64 { + mask :: 0x7ff; + shift :: 64 - 12; + bias :: 0x3ff; + + if f < 1 { + switch { + case f < 0: return -trunc_internal(-f); + case f == 0: return f; + case: return 0; + } + } + + x := transmute(u64)f; + e := (x >> shift) & mask - bias; + + if e < shift { + x &= ~(1 << (shift-e)) - 1; + } + return transmute(f64)x; + } + switch classify(x) { + case .Zero, .Neg_Zero, .NaN, .Inf, .Neg_Inf: + return x; + } + return trunc_internal(x); +} + +trunc :: proc{trunc_f32, trunc_f64}; + +round_f32 :: proc(x: f32) -> f32 { + return x < 0 ? ceil(x - 0.5) : floor(x + 0.5); +} +round_f64 :: proc(x: f64) -> f64 { + return x < 0 ? ceil(x - 0.5) : floor(x + 0.5); +} round :: proc{round_f32, round_f64}; + +ceil_f32 :: proc(x: f32) -> f32 { return -floor(-x); } +ceil_f64 :: proc(x: f64) -> f64 { return -floor(-x); } +ceil :: proc{ceil_f32, ceil_f64}; + floor_f32 :: proc(x: f32) -> f32 { if x == 0 || is_nan(x) || is_inf(x) { return x; @@ -144,33 +235,27 @@ floor_f64 :: proc(x: f64) -> f64 { } floor :: proc{floor_f32, floor_f64}; -ceil_f32 :: proc(x: f32) -> f32 { return -floor_f32(-x); } -ceil_f64 :: proc(x: f64) -> f64 { return -floor_f64(-x); } -ceil :: proc{ceil_f32, ceil_f64}; -remainder_f32 :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; } -remainder_f64 :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; } -remainder :: proc{remainder_f32, remainder_f64}; - -mod_f32 :: proc(x, y: f32) -> (n: f32) { - z := abs(y); - n = remainder(abs(x), z); - if sign(n) < 0 { - n += z; +floor_div :: proc(x, y: $T) -> T + where intrinsics.type_is_integer(T) { + a := x / y; + r := x % y; + if (r > 0 && y < 0) || (r < 0 && y > 0) { + a -= 1; } - return copy_sign(n, x); + return a; } -mod_f64 :: proc(x, y: f64) -> (n: f64) { - z := abs(y); - n = remainder(abs(x), z); - if sign(n) < 0 { - n += z; + +floor_mod :: proc(x, y: $T) -> T + where intrinsics.type_is_integer(T) { + r := x % y; + if (r > 0 && y < 0) || (r < 0 && y > 0) { + r += y; } - return copy_sign(n, x); + return r; } -mod :: proc{mod_f32, mod_f64}; -// TODO(bill): These need to implemented with the actual instructions + modf_f32 :: proc(x: f32) -> (int: f32, frac: f32) { shift :: 32 - 8 - 1; mask :: 0xff; @@ -190,8 +275,8 @@ modf_f32 :: proc(x: f32) -> (int: f32, frac: f32) { i := transmute(u32)x; e := uint(i>>shift)&mask - bias; - if e < 32-9 { - i &~= 1<<(32-9-e) - 1; + if e < shift { + i &~= 1<<(shift-e) - 1; } int = transmute(f32)i; frac = x - int; @@ -216,361 +301,275 @@ modf_f64 :: proc(x: f64) -> (int: f64, frac: f64) { i := transmute(u64)x; e := uint(i>>shift)&mask - bias; - if e < 64-12 { - i &~= 1<<(64-12-e) - 1; + if e < shift { + i &~= 1<<(shift-e) - 1; } int = transmute(f64)i; frac = x - int; return; } modf :: proc{modf_f32, modf_f64}; +split_decimal :: modf; -is_nan_f32 :: inline proc(x: f32) -> bool { return x != x; } -is_nan_f64 :: inline proc(x: f64) -> bool { return x != x; } -is_nan :: proc{is_nan_f32, is_nan_f64}; - -is_finite_f32 :: inline proc(x: f32) -> bool { return !is_nan(x-x); } -is_finite_f64 :: inline proc(x: f64) -> bool { return !is_nan(x-x); } -is_finite :: proc{is_finite_f32, is_finite_f64}; - -is_inf_f32 :: proc(x: f32, sign := 0) -> bool { - return sign >= 0 && x > F32_MAX || sign <= 0 && x < -F32_MAX; +mod_f32 :: proc(x, y: f32) -> (n: f32) { + z := abs(y); + n = remainder(abs(x), z); + if sign(n) < 0 { + n += z; + } + return copy_sign(n, x); } -is_inf_f64 :: proc(x: f64, sign := 0) -> bool { - return sign >= 0 && x > F64_MAX || sign <= 0 && x < -F64_MAX; +mod_f64 :: proc(x, y: f64) -> (n: f64) { + z := abs(y); + n = remainder(abs(x), z); + if sign(n) < 0 { + n += z; + } + return copy_sign(n, x); } -// If sign > 0, is_inf reports whether f is positive infinity -// If sign < 0, is_inf reports whether f is negative infinity -// If sign == 0, is_inf reports whether f is either infinity -is_inf :: proc{is_inf_f32, is_inf_f64}; - - - -to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360; } -to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; } - - - +mod :: proc{mod_f32, mod_f64}; -mul :: proc{ - mat3_mul, - mat4_mul, mat4_mul_vec4, - quat_mul, quat_mulf, -}; +remainder_f32 :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; } +remainder_f64 :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; } +remainder :: proc{remainder_f32, remainder_f64}; -div :: proc{quat_div, quat_divf}; -inverse :: proc{mat4_inverse, quat_inverse}; -dot :: proc{vec_dot, quat_dot}; -cross :: proc{cross2, cross3}; -vec_dot :: proc(a, b: $T/[$N]$E) -> E { - res: E; - for i in 0.. T + where intrinsics.type_is_ordered_numeric(T) { + x, y := x, y; + for y != 0 { + x %= y; + x, y = y, x; } - return res; + return abs(x); } -cross2 :: proc(a, b: $T/[2]$E) -> E { - return a[0]*b[1] - a[1]*b[0]; +lcm :: proc(x, y: $T) -> T + where intrinsics.type_is_ordered_numeric(T) { + return x / gcd(x, y) * y; } -cross3 :: proc(a, b: $T/[3]$E) -> T { - i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1); - j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0); - return T(i - j); +frexp_f32 :: proc(x: f32) -> (significand: f32, exponent: int) { + switch { + case x == 0: + return 0, 0; + case x < 0: + significand, exponent = frexp(-x); + return -significand, exponent; + } + ex := trunc(log2(x)); + exponent = int(ex); + significand = x / pow(2.0, ex); + if abs(significand) >= 1 { + exponent += 1; + significand /= 2; + } + if exponent == 1024 && significand == 0 { + significand = 0.99999999999999988898; + } + return; } +frexp_f64 :: proc(x: f64) -> (significand: f64, exponent: int) { + switch { + case x == 0: + return 0, 0; + case x < 0: + significand, exponent = frexp(-x); + return -significand, exponent; + } + ex := trunc(log2(x)); + exponent = int(ex); + significand = x / pow(2.0, ex); + if abs(significand) >= 1 { + exponent += 1; + significand /= 2; + } + if exponent == 1024 && significand == 0 { + significand = 0.99999999999999988898; + } + return; +} +frexp :: proc{frexp_f32, frexp_f64}; -length :: proc(v: $T/[$N]$E) -> E { return sqrt(dot(v, v)); } - -norm :: proc(v: $T/[$N]$E) -> T { return v / length(v); } -norm0 :: proc(v: $T/[$N]$E) -> T { - m := length(v); - return m == 0 ? 0 : v/m; -} +binomial :: proc(n, k: int) -> int { + switch { + case k <= 0: return 1; + case 2*k > n: return binomial(n, n-k); + } + b := n; + for i in 2.. int { + when size_of(int) == size_of(i64) { + @static table := [21]int{ + 1, + 1, + 2, + 6, + 24, + 120, + 720, + 5_040, + 40_320, + 362_880, + 3_628_800, + 39_916_800, + 479_001_600, + 6_227_020_800, + 87_178_291_200, + 1_307_674_368_000, + 20_922_789_888_000, + 355_687_428_096_000, + 6_402_373_705_728_000, + 121_645_100_408_832_000, + 2_432_902_008_176_640_000, + }; + } else { + @static table := [13]int{ + 1, + 1, + 2, + 6, + 24, + 120, + 720, + 5_040, + 40_320, + 362_880, + 3_628_800, + 39_916_800, + 479_001_600, + }; + } -identity :: proc($T: typeid/[$N][N]$E) -> T { - m: T; - for i in 0..= 0, "parameter must not be negative"); + assert(n < len(table), "parameter is too large to lookup in the table"); + return 0; } -transpose :: proc(m: $M/[$N][N]f32) -> M { - for j in 0.. Float_Class { + switch { + case x == 0: + i := transmute(i32)x; + if i < 0 { + return .Neg_Zero; } - } - return m; -} - -mat3_mul :: proc(a, b: Mat3) -> Mat3 { - c: Mat3; - for j in 0..<3 { - for i in 0..<3 { - c[j][i] = a[0][i]*b[j][0] + - a[1][i]*b[j][1] + - a[2][i]*b[j][2]; + return .Zero; + case x*0.5 == x: + if x < 0 { + return .Neg_Inf; } + return .Inf; + case x != x: + return .NaN; } - return c; -} -mat4_mul :: proc(a, b: Mat4) -> Mat4 { - c: Mat4; - for j in 0..<4 { - for i in 0..<4 { - c[j][i] = a[0][i]*b[j][0] + - a[1][i]*b[j][1] + - a[2][i]*b[j][2] + - a[3][i]*b[j][3]; + u := transmute(u32)x; + exp := int(u>>23) & (1<<8 - 1); + if exp == 0 { + return .Subnormal; + } + return .Normal; +} +classify_f64 :: proc(x: f64) -> Float_Class { + switch { + case x == 0: + i := transmute(i64)x; + if i < 0 { + return .Neg_Zero; + } + return .Zero; + case x*0.5 == x: + if x < 0 { + return .Neg_Inf; } + return .Inf; + case x != x: + return .NaN; } - return c; -} - -mat4_mul_vec4 :: proc(m: Mat4, v: Vec4) -> Vec4 { - return Vec4{ - m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0]*v[3], - m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1]*v[3], - m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2]*v[3], - m[0][3]*v[0] + m[1][3]*v[1] + m[2][3]*v[2] + m[3][3]*v[3], - }; -} - -mat4_inverse :: proc(m: Mat4) -> Mat4 { - o: Mat4; - - sf00 := m[2][2] * m[3][3] - m[3][2] * m[2][3]; - sf01 := m[2][1] * m[3][3] - m[3][1] * m[2][3]; - sf02 := m[2][1] * m[3][2] - m[3][1] * m[2][2]; - sf03 := m[2][0] * m[3][3] - m[3][0] * m[2][3]; - sf04 := m[2][0] * m[3][2] - m[3][0] * m[2][2]; - sf05 := m[2][0] * m[3][1] - m[3][0] * m[2][1]; - sf06 := m[1][2] * m[3][3] - m[3][2] * m[1][3]; - sf07 := m[1][1] * m[3][3] - m[3][1] * m[1][3]; - sf08 := m[1][1] * m[3][2] - m[3][1] * m[1][2]; - sf09 := m[1][0] * m[3][3] - m[3][0] * m[1][3]; - sf10 := m[1][0] * m[3][2] - m[3][0] * m[1][2]; - sf11 := m[1][1] * m[3][3] - m[3][1] * m[1][3]; - sf12 := m[1][0] * m[3][1] - m[3][0] * m[1][1]; - sf13 := m[1][2] * m[2][3] - m[2][2] * m[1][3]; - sf14 := m[1][1] * m[2][3] - m[2][1] * m[1][3]; - sf15 := m[1][1] * m[2][2] - m[2][1] * m[1][2]; - sf16 := m[1][0] * m[2][3] - m[2][0] * m[1][3]; - sf17 := m[1][0] * m[2][2] - m[2][0] * m[1][2]; - sf18 := m[1][0] * m[2][1] - m[2][0] * m[1][1]; - - - o[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02); - o[0][1] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04); - o[0][2] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05); - o[0][3] = -(m[1][0] * sf02 - m[1][1] * sf04 + m[1][2] * sf05); - - o[1][0] = -(m[0][1] * sf00 - m[0][2] * sf01 + m[0][3] * sf02); - o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04); - o[1][2] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05); - o[1][3] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05); - - o[2][0] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08); - o[2][1] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10); - o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12); - o[2][3] = -(m[0][0] * sf08 - m[0][1] * sf10 + m[0][2] * sf12); - - o[3][0] = -(m[0][1] * sf13 - m[0][2] * sf14 + m[0][3] * sf15); - o[3][1] = +(m[0][0] * sf13 - m[0][2] * sf16 + m[0][3] * sf17); - o[3][2] = -(m[0][0] * sf14 - m[0][1] * sf16 + m[0][3] * sf18); - o[3][3] = +(m[0][0] * sf15 - m[0][1] * sf17 + m[0][2] * sf18); - - ood := 1.0 / (m[0][0] * o[0][0] + - m[0][1] * o[0][1] + - m[0][2] * o[0][2] + - m[0][3] * o[0][3]); - - o[0][0] *= ood; - o[0][1] *= ood; - o[0][2] *= ood; - o[0][3] *= ood; - o[1][0] *= ood; - o[1][1] *= ood; - o[1][2] *= ood; - o[1][3] *= ood; - o[2][0] *= ood; - o[2][1] *= ood; - o[2][2] *= ood; - o[2][3] *= ood; - o[3][0] *= ood; - o[3][1] *= ood; - o[3][2] *= ood; - o[3][3] *= ood; - - return o; -} - - -mat4_translate :: proc(v: Vec3) -> Mat4 { - m := identity(Mat4); - m[3][0] = v[0]; - m[3][1] = v[1]; - m[3][2] = v[2]; - m[3][3] = 1; - return m; -} - -mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 { - c := cos(angle_radians); - s := sin(angle_radians); - - a := norm(v); - t := a * (1-c); - - rot := identity(Mat4); - - rot[0][0] = c + t[0]*a[0]; - rot[0][1] = 0 + t[0]*a[1] + s*a[2]; - rot[0][2] = 0 + t[0]*a[2] - s*a[1]; - rot[0][3] = 0; - - rot[1][0] = 0 + t[1]*a[0] - s*a[2]; - rot[1][1] = c + t[1]*a[1]; - rot[1][2] = 0 + t[1]*a[2] + s*a[0]; - rot[1][3] = 0; - - rot[2][0] = 0 + t[2]*a[0] + s*a[1]; - rot[2][1] = 0 + t[2]*a[1] - s*a[0]; - rot[2][2] = c + t[2]*a[2]; - rot[2][3] = 0; - - return rot; -} - -scale_vec3 :: proc(m: Mat4, v: Vec3) -> Mat4 { - mm := m; - mm[0][0] *= v[0]; - mm[1][1] *= v[1]; - mm[2][2] *= v[2]; - return mm; -} - -scale_f32 :: proc(m: Mat4, s: f32) -> Mat4 { - mm := m; - mm[0][0] *= s; - mm[1][1] *= s; - mm[2][2] *= s; - return mm; -} - -scale :: proc{scale_vec3, scale_f32}; - - -look_at :: proc(eye, centre, up: Vec3) -> Mat4 { - f := norm(centre - eye); - s := norm(cross(f, up)); - u := cross(s, f); - - return Mat4{ - {+s.x, +u.x, -f.x, 0}, - {+s.y, +u.y, -f.y, 0}, - {+s.z, +u.z, -f.z, 0}, - {-dot(s, eye), -dot(u, eye), dot(f, eye), 1}, - }; -} - -perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 { - m: Mat4; - tan_half_fovy := tan(0.5 * fovy); - - m[0][0] = 1.0 / (aspect*tan_half_fovy); - m[1][1] = 1.0 / (tan_half_fovy); - m[2][2] = -(far + near) / (far - near); - m[2][3] = -1.0; - m[3][2] = -2.0*far*near / (far - near); - return m; + u := transmute(u64)x; + exp := int(u>>52) & (1<<11 - 1); + if exp == 0 { + return .Subnormal; + } + return .Normal; } +classify :: proc{classify_f32, classify_f64}; +is_nan_f32 :: proc(x: f32) -> bool { return classify(x) == .NaN; } +is_nan_f64 :: proc(x: f64) -> bool { return classify(x) == .NaN; } +is_nan :: proc{is_nan_f32, is_nan_f64}; -ortho3d :: proc(left, right, bottom, top, near, far: f32) -> Mat4 { - m := identity(Mat4); - m[0][0] = +2.0 / (right - left); - m[1][1] = +2.0 / (top - bottom); - m[2][2] = -2.0 / (far - near); - m[3][0] = -(right + left) / (right - left); - m[3][1] = -(top + bottom) / (top - bottom); - m[3][2] = -(far + near) / (far - near); - return m; -} - +is_inf_f32 :: proc(x: f32) -> bool { return classify(abs(x)) == .Inf; } +is_inf_f64 :: proc(x: f64) -> bool { return classify(abs(x)) == .Inf; } +is_inf :: proc{is_inf_f32, is_inf_f64}; -// Quaternion operations -conj :: proc(q: Quat) -> Quat { - return Quat{-q.x, -q.y, -q.z, q.w}; -} -quat_mul :: proc(q0, q1: Quat) -> Quat { - d: Quat; - d.x = q0.w * q1.x + q0.x * q1.w + q0.y * q1.z - q0.z * q1.y; - d.y = q0.w * q1.y - q0.x * q1.z + q0.y * q1.w + q0.z * q1.x; - d.z = q0.w * q1.z + q0.x * q1.y - q0.y * q1.x + q0.z * q1.w; - d.w = q0.w * q1.w - q0.x * q1.x - q0.y * q1.y - q0.z * q1.z; - return d; +is_power_of_two :: proc(x: int) -> bool { + return x > 0 && (x & (x-1)) == 0; } -quat_mulf :: proc(q: Quat, f: f32) -> Quat { return Quat{q.x*f, q.y*f, q.z*f, q.w*f}; } -quat_divf :: proc(q: Quat, f: f32) -> Quat { return Quat{q.x/f, q.y/f, q.z/f, q.w/f}; } - -quat_div :: proc(q0, q1: Quat) -> Quat { return mul(q0, quat_inverse(q1)); } -quat_inverse :: proc(q: Quat) -> Quat { return div(conj(q), dot(q, q)); } -quat_dot :: proc(q0, q1: Quat) -> f32 { return q0.x*q1.x + q0.y*q1.y + q0.z*q1.z + q0.w*q1.w; } - -quat_norm :: proc(q: Quat) -> Quat { - m := sqrt(dot(q, q)); - return div(q, m); +next_power_of_two :: proc(x: int) -> int { + k := x -1; + when size_of(int) == 8 { + k = k | (k >> 32); + } + k = k | (k >> 16); + k = k | (k >> 8); + k = k | (k >> 4); + k = k | (k >> 2); + k = k | (k >> 1); + k += 1 + int(x <= 0); + return k; +} + +sum :: proc(x: $T/[]$E) -> (res: E) + where intrinsics.BuiltinProc_type_is_numeric(E) { + for i in x { + res += i; + } + return; } -axis_angle :: proc(axis: Vec3, angle_radians: f32) -> Quat { - v := norm(axis) * sin(0.5*angle_radians); - w := cos(0.5*angle_radians); - return Quat{v.x, v.y, v.z, w}; +prod :: proc(x: $T/[]$E) -> (res: E) + where intrinsics.BuiltinProc_type_is_numeric(E) { + for i in x { + res *= i; + } + return; } -euler_angles :: proc(pitch, yaw, roll: f32) -> Quat { - p := axis_angle(Vec3{1, 0, 0}, pitch); - y := axis_angle(Vec3{0, 1, 0}, yaw); - r := axis_angle(Vec3{0, 0, 1}, roll); - return mul(mul(y, p), r); +cumsum_inplace :: proc(x: $T/[]$E) -> T + where intrinsics.BuiltinProc_type_is_numeric(E) { + for i in 1.. Mat4 { - a := quat_norm(q); - xx := a.x*a.x; yy := a.y*a.y; zz := a.z*a.z; - xy := a.x*a.y; xz := a.x*a.z; yz := a.y*a.z; - wx := a.w*a.x; wy := a.w*a.y; wz := a.w*a.z; - - m := identity(Mat4); - - m[0][0] = 1 - 2*(yy + zz); - m[0][1] = 2*(xy + wz); - m[0][2] = 2*(xz - wy); - m[1][0] = 2*(xy - wz); - m[1][1] = 1 - 2*(xx + zz); - m[1][2] = 2*(yz + wx); - - m[2][0] = 2*(xz + wy); - m[2][1] = 2*(yz - wx); - m[2][2] = 1 - 2*(xx + yy); - return m; +cumsum :: proc(dst, src: $T/[]$E) -> T + where intrinsics.BuiltinProc_type_is_numeric(E) { + N := min(len(dst), len(src)); + if N > 0 { + dst[0] = src[0]; + for i in 1..inlining != ProcInlining_none) { error(call, "Inlining operators are not allowed on built-in procedures"); @@ -3882,6 +3882,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } operand->mode = Addressing_Value; + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + break; } @@ -3945,6 +3949,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 default: GB_PANIC("Invalid type"); break; } + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + break; } @@ -4033,6 +4041,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 default: GB_PANIC("Invalid type"); break; } + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + break; } @@ -4087,6 +4099,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 default: GB_PANIC("Invalid type"); break; } + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + break; } @@ -4134,6 +4150,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 default: GB_PANIC("Invalid type"); break; } + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + break; } @@ -6363,7 +6383,7 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper -ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { +ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) { ast_node(ce, CallExpr, call); if (ce->proc != nullptr && ce->proc->kind == Ast_BasicDirective) { @@ -6470,7 +6490,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { if (operand->mode == Addressing_Builtin) { i32 id = operand->builtin_id; - if (!check_builtin_procedure(c, operand, call, id)) { + if (!check_builtin_procedure(c, operand, call, id, type_hint)) { operand->mode = Addressing_Invalid; } operand->expr = call; @@ -7930,7 +7950,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(ce, CallExpr, node); - return check_call_expr(c, o, node); + return check_call_expr(c, o, node, type_hint); case_end; case_ast_node(de, DerefExpr, node); -- cgit v1.2.3 From 416ff149bd0f3ae1ff7d8093450cf42fc4aa3994 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 27 Oct 2019 19:42:21 +0000 Subject: Fix procedure group "best fit" algorithm for polymorphic procedures --- core/math/linalg/linalg.odin | 27 ++++++++++++++++++++++++--- src/check_expr.cpp | 34 ++++++++++++++++++---------------- src/check_type.cpp | 7 ++++--- src/checker.hpp | 1 + 4 files changed, 47 insertions(+), 22 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/math/linalg/linalg.odin b/core/math/linalg/linalg.odin index 66a11b47a..ceef645cb 100644 --- a/core/math/linalg/linalg.odin +++ b/core/math/linalg/linalg.odin @@ -70,7 +70,7 @@ identity :: proc($T: typeid/[$N][N]$E) -> (m: T) { return m; } -transpose :: proc(a: $T/[$N][$M]$E) -> (m: ((M == N) ? T : [M][N]E)) { +transpose :: proc(a: $T/[$N][$M]$E) -> (m: [M][N]E) { for j in 0.. (m: ((M == N) ? T : [M][N]E)) { return; } -mul_matrix :: proc(a: $A/[$I][$J]$E, b: $B/[J][$K]E) -> (c: ((I == J && J == K && A == B) ? A : [I][K]E)) +mul_matrix :: proc(a, b: $M/[$N][N]$E) -> (c: M) where !intrinsics.type_is_array(E), intrinsics.type_is_numeric(E) { + for i in 0.. (c: [I][K]E) + where !intrinsics.type_is_array(E), + intrinsics.type_is_numeric(E), + I != J { for i in 0.. (c: ((I == J && J == K & return; } + mul_matrix_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B) where !intrinsics.type_is_array(E), intrinsics.type_is_numeric(E) { @@ -124,7 +139,13 @@ mul_quaternion256_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V { } mul_quaternion_vector3 :: proc{mul_quaternion128_vector3, mul_quaternion256_vector3}; -mul :: proc{mul_matrix, mul_matrix_vector, mul_quaternion128_vector3, mul_quaternion256_vector3}; +mul :: proc{ + mul_matrix, + mul_matrix_differ, + mul_matrix_vector, + mul_quaternion128_vector3, + mul_quaternion256_vector3, +}; // Specific diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a3b8befa9..b5d24e008 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5855,6 +5855,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } + if (proc_arg_count >= 0 && proc_arg_count_all_equal) { lhs_count = proc_arg_count; if (lhs_count > 0) { @@ -5898,9 +5899,8 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type gb_free(heap_allocator(), lhs); } - ValidIndexAndScore *valids = gb_alloc_array(heap_allocator(), ValidIndexAndScore, procs.count); - isize valid_count = 0; - defer (gb_free(heap_allocator(), valids)); + auto valids = array_make(heap_allocator(), 0, procs.count); + defer (array_free(&valids)); gbString expr_name = expr_to_string(operand->expr); defer (gb_string_free(expr_name)); @@ -5915,12 +5915,14 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type ctx.no_polymorphic_errors = true; ctx.allow_polymorphic_types = is_type_polymorphic(pt); + ctx.hide_polymorphic_errors = true; err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data); if (err != CallArgumentError_None) { continue; } + if (data.gen_entity != nullptr) { Entity *e = data.gen_entity; DeclInfo *decl = data.gen_entity->decl_info; @@ -5936,31 +5938,31 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } } - valids[valid_count].index = i; - valids[valid_count].score = data.score; - valid_count++; + ValidIndexAndScore item = {}; + item.index = i; + item.score = data.score; + array_add(&valids, item); } } - if (valid_count > 1) { - gb_sort_array(valids, valid_count, valid_index_and_score_cmp); + if (valids.count > 1) { + gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp); i64 best_score = valids[0].score; Entity *best_entity = procs[valids[0].index]; - for (isize i = 1; i < valid_count; i++) { + for (isize i = 1; i < valids.count; i++) { if (best_score > valids[i].score) { - valid_count = i; + valids.count = i; break; } if (best_entity == procs[valids[i].index]) { - valid_count = i; + valids.count = i; break; } - best_score = valids[i].score; } } - if (valid_count == 0) { + if (valids.count == 0) { begin_error_block(); defer (end_error_block()); @@ -6015,7 +6017,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } result_type = t_invalid; - } else if (valid_count > 1) { + } else if (valids.count > 1) { begin_error_block(); defer (end_error_block()); @@ -6030,11 +6032,11 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } error_line(")\n"); - for (isize i = 0; i < valid_count; i++) { + for (isize i = 0; i < valids.count; i++) { Entity *proc = procs[valids[i].index]; TokenPos pos = proc->token.pos; Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc); - gbString pt; + gbString pt = nullptr; defer (gb_string_free(pt)); if (t->Proc.node != nullptr) { pt = expr_to_string(t->Proc.node); diff --git a/src/check_type.cpp b/src/check_type.cpp index da6419877..de33cc1a5 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1263,20 +1263,21 @@ bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Typ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand operand) { bool modify_type = !ctx->no_polymorphic_errors; + bool show_error = modify_type && !ctx->hide_polymorphic_errors; if (!is_operand_value(operand)) { - if (modify_type) { + if (show_error) { error(operand.expr, "Cannot determine polymorphic type from parameter"); } return t_invalid; } if (is_polymorphic_type_assignable(ctx, poly_type, operand.type, false, modify_type)) { - if (modify_type) { + if (show_error) { set_procedure_abi_types(ctx, poly_type); } return poly_type; } - if (modify_type) { + if (show_error) { gbString pts = type_to_string(poly_type); gbString ots = type_to_string(operand.type); defer (gb_string_free(pts)); diff --git a/src/checker.hpp b/src/checker.hpp index f86acce5c..c33514511 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -285,6 +285,7 @@ struct CheckerContext { bool collect_delayed_decls; bool allow_polymorphic_types; bool no_polymorphic_errors; + bool hide_polymorphic_errors; bool in_polymorphic_specialization; Scope * polymorphic_scope; }; -- cgit v1.2.3 From ee8d3e03f89b1f8065fc9563d84830482bc3f387 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 31 Oct 2019 18:25:39 +0000 Subject: Delay determination of procedure abi types until as late as possible to prevent type undetermination in self-referential data types #454 --- src/check_expr.cpp | 4 ++-- src/check_type.cpp | 20 ++++++++++++-------- src/ir.cpp | 3 +++ src/ir_print.cpp | 5 +++++ src/types.cpp | 9 +++++++-- 5 files changed, 29 insertions(+), 12 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b5d24e008..51497af7e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -89,7 +89,7 @@ Type * check_init_variable (CheckerContext *c, Entity *e, Operand * Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCallingConvention cc); Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCallingConvention cc); bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type *abi_return_type); -void set_procedure_abi_types(CheckerContext *c, Type *type); +void set_procedure_abi_types(gbAllocator a, Type *type); void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type); Entity *entity_from_expr(Ast *expr) { @@ -963,7 +963,7 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, } if (modify_type) { - set_procedure_abi_types(c, source); + set_procedure_abi_types(c->allocator, source); } return true; diff --git a/src/check_type.cpp b/src/check_type.cpp index de33cc1a5..45c59f2bc 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1273,7 +1273,7 @@ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Oper if (is_polymorphic_type_assignable(ctx, poly_type, operand.type, false, modify_type)) { if (show_error) { - set_procedure_abi_types(ctx, poly_type); + set_procedure_abi_types(ctx->allocator, poly_type); } return poly_type; } @@ -2362,18 +2362,22 @@ bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type return false; } -void set_procedure_abi_types(CheckerContext *c, Type *type) { +void set_procedure_abi_types(gbAllocator allocator, Type *type) { type = base_type(type); if (type->kind != Type_Proc) { return; } - type->Proc.abi_compat_params = array_make(c->allocator, cast(isize)type->Proc.param_count); + if (type->Proc.abi_types_set) { + return; + } + + type->Proc.abi_compat_params = array_make(allocator, cast(isize)type->Proc.param_count); for (i32 i = 0; i < type->Proc.param_count; i++) { Entity *e = type->Proc.params->Tuple.variables[i]; if (e->kind == Entity_Variable) { Type *original_type = e->type; - Type *new_type = type_to_abi_compat_param_type(c->allocator, original_type, type->Proc.calling_convention); + Type *new_type = type_to_abi_compat_param_type(allocator, original_type, type->Proc.calling_convention); type->Proc.abi_compat_params[i] = new_type; switch (type->Proc.calling_convention) { case ProcCC_Odin: @@ -2387,8 +2391,10 @@ void set_procedure_abi_types(CheckerContext *c, Type *type) { } // NOTE(bill): The types are the same - type->Proc.abi_compat_result_type = type_to_abi_compat_result_type(c->allocator, type->Proc.results, type->Proc.calling_convention); - type->Proc.return_by_pointer = abi_compat_return_by_pointer(c->allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type); + type->Proc.abi_compat_result_type = type_to_abi_compat_result_type(allocator, type->Proc.results, type->Proc.calling_convention); + type->Proc.return_by_pointer = abi_compat_return_by_pointer(allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type); + + type->Proc.abi_types_set = true; } // NOTE(bill): 'operands' is for generating non generic procedure type @@ -2486,8 +2492,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, } type->Proc.is_polymorphic = is_polymorphic; - set_procedure_abi_types(c, type); - return success; } diff --git a/src/ir.cpp b/src/ir.cpp index ae60be7e3..d4aecebf0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3038,6 +3038,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array const &ar context_ptr = ir_find_or_generate_context_ptr(p); } + set_procedure_abi_types(heap_allocator(), pt); bool is_c_vararg = pt->Proc.c_vararg; isize param_count = pt->Proc.param_count; @@ -10019,6 +10020,8 @@ void ir_insert_code_before_proc(irProcedure* proc, irProcedure *parent) { void ir_build_proc(irValue *value, irProcedure *parent) { irProcedure *proc = &value->Proc; + set_procedure_abi_types(heap_allocator(), proc->type); + proc->parent = parent; if (proc->body != nullptr) { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 1d0c8af35..d47dfc898 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -323,12 +323,15 @@ void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) { void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) { + set_procedure_abi_types(heap_allocator(), t); + i64 word_bits = 8*build_context.word_size; t = base_type(t); GB_ASSERT(is_type_proc(t)); isize param_count = t->Proc.param_count; isize result_count = t->Proc.result_count; + ir_print_proc_results(f, m, t); ir_write_string(f, str_lit(" (")); if (t->Proc.return_by_pointer) { @@ -2125,6 +2128,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { + set_procedure_abi_types(heap_allocator(), proc->type); + if (proc->body == nullptr) { ir_write_str_lit(f, "declare "); // if (proc->tags & ProcTag_dll_import) { diff --git a/src/types.cpp b/src/types.cpp index 8ad352f62..bef69ee30 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -205,6 +205,7 @@ struct TypeUnion { Type * abi_compat_result_type; \ i32 variadic_index; \ bool variadic; \ + bool abi_types_set; \ bool require_results; \ bool c_vararg; \ bool is_polymorphic; \ @@ -2360,7 +2361,9 @@ i64 type_size_of(Type *t) { return 0; } // NOTE(bill): Always calculate the size when it is a Type_Basic - if (t->kind != Type_Basic && t->cached_size >= 0) { + if (t->kind == Type_Named && t->cached_size >= 0) { + + } else if (t->kind != Type_Basic && t->cached_size >= 0) { return t->cached_size; } TypePath path = {0}; @@ -2375,7 +2378,9 @@ i64 type_align_of(Type *t) { return 1; } // NOTE(bill): Always calculate the size when it is a Type_Basic - if (t->kind != Type_Basic && t->cached_align > 0) { + if (t->kind == Type_Named && t->cached_align >= 0) { + + } if (t->kind != Type_Basic && t->cached_align > 0) { return t->cached_align; } -- cgit v1.2.3 From 01dfb1dac8b71b2b84bb585395063c52779f5674 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 31 Oct 2019 20:17:50 +0000 Subject: Fix double calling of lhs of logical binary expressions --- src/check_expr.cpp | 8 ++++++-- src/check_type.cpp | 29 +++++++++++++++++++---------- src/ir.cpp | 15 ++++++++------- 3 files changed, 33 insertions(+), 19 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 51497af7e..8612167c9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -62,7 +62,7 @@ Type * make_optional_ok_type (Type *value); void check_type_decl (CheckerContext *c, Entity *e, Ast *type_expr, Type *def); Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint); Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name); -Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array ordered_operands); +Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array const &ordered_operands, bool *failure); void check_not_tuple (CheckerContext *c, Operand *operand); void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type); gbString expr_to_string (Ast *expression); @@ -6336,12 +6336,16 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper { gbAllocator a = c->allocator; - Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands); + bool failure = false; + Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure); if (found_entity) { operand->mode = Addressing_Type; operand->type = found_entity->type; return err; } + if (failure) { + return CallArgumentError_NoneConstantParameter; + } String generated_name = make_string_c(expr_to_string(call)); diff --git a/src/check_type.cpp b/src/check_type.cpp index 45c59f2bc..ef68203ab 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -248,38 +248,47 @@ bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) { } -Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array ordered_operands) { +Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array const &ordered_operands, bool *failure) { auto *found_gen_types = map_get(&ctx->checker->info.gen_types, hash_pointer(original_type)); if (found_gen_types != nullptr) { for_array(i, *found_gen_types) { Entity *e = (*found_gen_types)[i]; Type *t = base_type(e->type); TypeTuple *tuple = get_record_polymorphic_params(t); - bool ok = true; GB_ASSERT(param_count == tuple->variables.count); + + bool skip = false; + for (isize j = 0; j < param_count; j++) { Entity *p = tuple->variables[j]; Operand o = ordered_operands[j]; if (p->kind == Entity_TypeName) { if (is_type_polymorphic(o.type)) { // NOTE(bill): Do not add polymorphic version to the gen_types - ok = false; - } - if (!are_types_identical(o.type, p->type)) { - ok = false; + skip = true; + break; + } else if (!are_types_identical(o.type, p->type)) { + skip = true; + break; } } else if (p->kind == Entity_Constant) { - if (!are_types_identical(o.type, p->type)) { - ok = false; + if (o.mode != Addressing_Constant) { + if (failure) *failure = true; + skip = true; + break; } if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) { - ok = false; + skip = true; + break; + } else if (!are_types_identical(o.type, p->type)) { + skip = true; + break; } } else { GB_PANIC("Unknown entity kind"); } } - if (ok) { + if (!skip) { return e; } } diff --git a/src/ir.cpp b/src/ir.cpp index d4aecebf0..e7317a960 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5647,7 +5647,8 @@ irValue *ir_emit_logical_binary_expr(irProcedure *proc, TokenKind op, Ast *left, } ir_start_block(proc, rhs); - array_add(&edges, ir_build_expr(proc, right)); + irValue *edge = ir_build_expr(proc, right); + array_add(&edges, edge); ir_emit_jump(proc, done); ir_start_block(proc, done); @@ -5656,8 +5657,6 @@ irValue *ir_emit_logical_binary_expr(irProcedure *proc, TokenKind op, Ast *left, irValue *ir_emit_logical_binary_expr(irProcedure *proc, Ast *expr) { ast_node(be, BinaryExpr, expr); - irBlock *rhs = ir_new_block(proc, nullptr, "logical.cmp.rhs"); - irBlock *done = ir_new_block(proc, nullptr, "logical.cmp.done"); Type *type = type_of_expr(expr); type = default_type(type); @@ -6879,9 +6878,6 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case_end; case_ast_node(be, BinaryExpr, expr); - irValue *left = ir_build_expr(proc, be->left); - Type *type = default_type(tv.type); - switch (be->op.kind) { case Token_Add: case Token_Sub: @@ -6895,6 +6891,8 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case Token_AndNot: case Token_Shl: case Token_Shr: { + irValue *left = ir_build_expr(proc, be->left); + Type *type = default_type(tv.type); irValue *right = ir_build_expr(proc, be->right); return ir_emit_arith(proc, be->op.kind, left, right, type); } @@ -6906,10 +6904,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case Token_LtEq: case Token_Gt: case Token_GtEq: { + irValue *left = ir_build_expr(proc, be->left); + Type *type = default_type(tv.type); irValue *right = ir_build_expr(proc, be->right); irValue *cmp = ir_emit_comp(proc, be->op.kind, left, right); return ir_emit_conv(proc, cmp, type); - break; } case Token_CmpAnd: @@ -6919,6 +6918,8 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case Token_in: case Token_notin: { + irValue *left = ir_build_expr(proc, be->left); + Type *type = default_type(tv.type); irValue *right = ir_build_expr(proc, be->right); Type *rt = base_type(ir_type(right)); switch (rt->kind) { -- cgit v1.2.3 From 560bdc339b1137ab6f52878a13dce0a4742b6153 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 31 Oct 2019 22:39:12 +0000 Subject: Fix stack overflow bug caused by polymorphic record with polymorphic constant parameters. #447 DOES NOT FIX THE UNDERLYING BUG --- src/check_expr.cpp | 8 ++++---- src/check_type.cpp | 18 +++++++++++------- 2 files changed, 15 insertions(+), 11 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8612167c9..254a7ce51 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6343,9 +6343,8 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper operand->type = found_entity->type; return err; } - if (failure) { - return CallArgumentError_NoneConstantParameter; - } + + String generated_name = make_string_c(expr_to_string(call)); @@ -6458,7 +6457,8 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t Ast *s = ident->SelectorExpr.selector; ident = s; } - Type *ot = operand->type; GB_ASSERT(ot->kind == Type_Named); + Type *ot = operand->type; + GB_ASSERT(ot->kind == Type_Named); Entity *e = ot->Named.type_name; add_entity_use(c, ident, e); add_type_and_value(&c->checker->info, call, Addressing_Type, ot, empty_exact_value); diff --git a/src/check_type.cpp b/src/check_type.cpp index ef68203ab..cf5855a3e 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -262,25 +262,29 @@ Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, for (isize j = 0; j < param_count; j++) { Entity *p = tuple->variables[j]; Operand o = ordered_operands[j]; + Entity *oe = entity_of_node(o.expr); + if (p == oe) { + // NOTE(bill): This is the same type, make sure that it will be be same thing and use that + // Saves on a lot of checking too below + continue; + } + if (p->kind == Entity_TypeName) { if (is_type_polymorphic(o.type)) { // NOTE(bill): Do not add polymorphic version to the gen_types skip = true; break; - } else if (!are_types_identical(o.type, p->type)) { - skip = true; - break; } - } else if (p->kind == Entity_Constant) { - if (o.mode != Addressing_Constant) { - if (failure) *failure = true; + if (!are_types_identical(o.type, p->type)) { skip = true; break; } + } else if (p->kind == Entity_Constant) { if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) { skip = true; break; - } else if (!are_types_identical(o.type, p->type)) { + } + if (!are_types_identical(o.type, p->type)) { skip = true; break; } -- cgit v1.2.3 From a20c31d6b509076141fe69f33d2fe3b465122f78 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 31 Oct 2019 22:58:38 +0000 Subject: Fix polymorphic record parameter determination bug caused by polymorphic constants not being handled correctly #447 --- src/check_expr.cpp | 2 -- src/check_type.cpp | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 254a7ce51..fae38fa01 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6344,8 +6344,6 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper return err; } - - String generated_name = make_string_c(expr_to_string(call)); CheckerContext ctx = *c; diff --git a/src/check_type.cpp b/src/check_type.cpp index cf5855a3e..9f8310e44 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -461,6 +461,10 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< e = alloc_entity_type_name(scope, token, operand.type); e->TypeName.is_type_alias = true; } else { + if (is_type_polymorphic(base_type(operand.type))) { + is_polymorphic = true; + can_check_fields = false; + } e = alloc_entity_constant(scope, token, operand.type, operand.value); } } else { -- cgit v1.2.3