aboutsummaryrefslogtreecommitdiff
path: root/src/check_expr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/check_expr.cpp')
-rw-r--r--src/check_expr.cpp1607
1 files changed, 1052 insertions, 555 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index bd1c34044..251f196a5 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -129,6 +129,8 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
gb_internal bool is_exact_value_zero(ExactValue const &v);
+gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node);
+
enum LoadDirectiveResult {
LoadDirective_Success = 0,
LoadDirective_Error = 1,
@@ -263,6 +265,9 @@ gb_internal void check_did_you_mean_scope(String const &name, Scope *scope, char
gb_internal Entity *entity_from_expr(Ast *expr) {
expr = unparen_expr(expr);
+ if (expr == nullptr) {
+ return nullptr;
+ }
switch (expr->kind) {
case Ast_Ident:
return expr->Ident.entity;
@@ -582,6 +587,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
d->proc_lit = proc_lit;
d->proc_checked_state = ProcCheckedState_Unchecked;
d->defer_use_checked = false;
+ d->para_poly_original = old_decl->entity;
Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags);
entity->state.store(EntityState_Resolved);
@@ -603,7 +609,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
entity->flags |= EntityFlag_Disabled;
}
- d->entity = entity;
+ d->entity.store(entity);
AstFile *file = nullptr;
{
@@ -643,7 +649,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
gb_internal bool check_polymorphic_procedure_assignment(CheckerContext *c, Operand *operand, Type *type, Ast *poly_def_node, PolyProcData *poly_proc_data) {
if (operand->expr == nullptr) return false;
- Entity *base_entity = entity_of_node(operand->expr);
+ Entity *base_entity = entity_from_expr(operand->expr);
if (base_entity == nullptr) return false;
return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data);
}
@@ -816,9 +822,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
}
- if (is_type_enum(dst) && are_types_identical(dst->Enum.base_type, operand->type)) {
- if (c->in_enum_type) {
- return 3;
+ if (c != nullptr) {
+ if (is_type_enum(dst) && are_types_identical(dst->Enum.base_type, operand->type)) {
+ if (c->in_enum_type) {
+ return 3;
+ }
}
}
@@ -863,6 +871,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
if (are_types_identical(vt, s)) {
return 1;
}
+ if (is_type_proc(vt)) {
+ if (are_types_identical(base_type(vt), src)) {
+ return 1;
+ }
+ }
}
if (dst->Union.variants.count == 1) {
@@ -990,14 +1003,34 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) {
gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) {
- i64 score = 0;
- i64 distance = check_distance_between_types(c, operand, type, allow_array_programming);
- bool ok = distance >= 0;
- if (ok) {
- score = assign_score_function(distance, is_variadic);
+ if (c == nullptr) {
+ GB_ASSERT(operand->mode == Addressing_Value);
+ GB_ASSERT(is_type_typed(operand->type));
}
- if (score_) *score_ = score;
- return ok;
+ if (operand->mode == Addressing_Invalid || type == t_invalid) {
+ if (score_) *score_ = 0;
+ return false;
+ }
+
+ // Handle polymorphic procedure used as default parameter
+ if (operand->mode == Addressing_Value && is_type_proc(type) && is_type_proc(operand->type)) {
+ Entity *e = entity_from_expr(operand->expr);
+ if (e != nullptr && e->kind == Entity_Procedure && is_type_polymorphic(e->type) && !is_type_polymorphic(type)) {
+ // Special case: Allow a polymorphic procedure to be used as default value for concrete proc type
+ // during the initial check. It will be properly instantiated when actually used.
+ if (score_) *score_ = assign_score_function(1);
+ return true;
+ }
+ }
+
+ i64 score = check_distance_between_types(c, operand, type, allow_array_programming);
+ if (score >= 0) {
+ if (score_) *score_ = assign_score_function(score, is_variadic);
+ return true;
+ }
+
+ if (score_) *score_ = 0;
+ return false;
}
@@ -1708,7 +1741,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
o->expr = n;
String name = n->Ident.token.string;
- Entity *e = scope_lookup(c->scope, name);
+ Entity *e = scope_lookup(c->scope, name, n->Ident.hash);
if (e == nullptr) {
if (is_blank_ident(name)) {
error(n, "'_' cannot be used as a value");
@@ -1849,7 +1882,10 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
o->type = t_invalid;
}
if (o->type != nullptr && o->type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
- o->type = base_type(o->type);
+ Type *bt = base_type(o->type);
+ if (bt != nullptr) {
+ o->type = bt;
+ }
}
break;
@@ -2075,6 +2111,9 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
} else if (is_type_boolean(type)) {
return in_value.kind == ExactValue_Bool;
} else if (is_type_string(type)) {
+ if (in_value.kind == ExactValue_String16) {
+ return is_type_string16(type) || is_type_cstring16(type);
+ }
return in_value.kind == ExactValue_String;
} else if (is_type_integer(type) || is_type_rune(type)) {
if (in_value.kind == ExactValue_Bool) {
@@ -2289,11 +2328,28 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
if (in_value.kind == ExactValue_String) {
return false;
}
+ if (in_value.kind == ExactValue_String16) {
+ return false;
+ }
if (out_value) *out_value = in_value;
} else if (is_type_bit_set(type)) {
if (in_value.kind == ExactValue_Integer) {
return true;
}
+ } else if (is_type_typeid(type)) {
+
+ if (in_value.kind == ExactValue_Compound) {
+ ast_node(cl, CompoundLit, in_value.value_compound);
+ if (cl->elems.count == 0) {
+ in_value = exact_value_typeid(nullptr);
+ } else {
+ return false;
+ }
+ }
+ if (in_value.kind == ExactValue_Typeid) {
+ if (out_value) *out_value = in_value;
+ return true;
+ }
}
return false;
@@ -2393,27 +2449,27 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o
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);
+ error_line("\tSuggestion: The array expression may be sliced with %s[:]\n", a);
}
} else if (is_type_dynamic_array(src) && is_type_slice(dst)) {
Type *s = src->DynamicArray.elem;
Type *d = dst->Slice.elem;
if (are_types_identical(s, d)) {
- error_line("\tSuggestion: the dynamic array expression may be sliced with %s[:]\n", a);
+ error_line("\tSuggestion: The dynamic array expression may be sliced with %s[:]\n", a);
}
}else if (are_types_identical(src, dst) && !are_types_identical(o->type, type)) {
- error_line("\tSuggestion: the expression may be directly casted to type %s\n", b);
+ 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 transmuted to %s\n", b);
- error_line("\t This is an UNSAFE operation as string data is assumed to be immutable, \n");
+ error_line("\tSuggestion: A string may be transmuted to %s\n", b);
+ error_line("\t This is an UNSAFE operation as string data is assumed to be immutable,\n");
error_line("\t whereas slices in general are assumed to be mutable.\n");
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
- error_line("\tSuggestion: the expression may be casted to %s\n", b);
+ error_line("\tSuggestion: The expression may be casted to %s\n", b);
} else if (check_integer_exceed_suggestion(c, o, type, max_bit_size)) {
return;
} else if (is_expr_inferred_fixed_array(c->type_hint_expr) && is_type_array_like(type) && is_type_array_like(o->type)) {
gbString s = expr_to_string(c->type_hint_expr);
- error_line("\tSuggestion: make sure that `%s` is attached to the compound literal directly\n", s);
+ error_line("\tSuggestion: Make sure that `%s` is attached to the compound literal directly\n", s);
gb_string_free(s);
} else if (is_type_pointer(type) &&
o->mode == Addressing_Variable &&
@@ -2424,7 +2480,8 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o
} else if (is_type_pointer(o->type) &&
are_types_identical(type_deref(o->type), type)) {
gbString s = expr_to_string(o->expr);
- error_line("\tSuggestion: Did you mean `%s^`\n", s);
+ if (s[0] == '&') error_line("\tSuggestion: Did you mean `%s`\n", &s[1]);
+ else error_line("\tSuggestion: Did you mean `%s^`\n", s);
gb_string_free(s);
}
}
@@ -2599,9 +2656,8 @@ gb_internal ExactValue exact_bit_set_all_set_mask(Type *type) {
continue;
}
- BigInt shift_amount = f->Constant.value.value_integer;
- big_int_sub_eq(&shift_amount, &b_lower_base);
-
+ BigInt shift_amount = {};
+ big_int_sub(&shift_amount, &f->Constant.value.value_integer, &b_lower_base);
BigInt value = {};
big_int_shl(&value, &one, &shift_amount);
@@ -2832,6 +2888,14 @@ gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t
add_package_dependency(c, "runtime", "string_eq");
add_package_dependency(c, "runtime", "string_ne");
break;
+ case Basic_cstring16:
+ add_package_dependency(c, "runtime", "cstring16_eq");
+ add_package_dependency(c, "runtime", "cstring16_ne");
+ break;
+ case Basic_string16:
+ add_package_dependency(c, "runtime", "string16_eq");
+ add_package_dependency(c, "runtime", "string16_ne");
+ break;
}
break;
case Type_Struct:
@@ -2903,9 +2967,20 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper
if (!defined) {
gbString xs = type_to_string(x->type, temporary_allocator());
gbString ys = type_to_string(y->type, temporary_allocator());
- err_str = gb_string_make(temporary_allocator(),
- gb_bprintf("operator '%.*s' not defined between the types '%s' and '%s'", LIT(token_strings[op]), xs, ys)
- );
+
+ if (!is_type_comparable(x->type)) {
+ err_str = gb_string_make(temporary_allocator(),
+ gb_bprintf("Type '%s' is not simply comparable, so operator '%.*s' is not defined for it", xs, LIT(token_strings[op]))
+ );
+ } else if (!is_type_comparable(y->type)) {
+ err_str = gb_string_make(temporary_allocator(),
+ gb_bprintf("Type '%s' is not simply comparable, so operator '%.*s' is not defined for it", ys, LIT(token_strings[op]))
+ );
+ } else {
+ err_str = gb_string_make(temporary_allocator(),
+ gb_bprintf("Operator '%.*s' not defined between the types '%s' and '%s'", LIT(token_strings[op]), xs, ys)
+ );
+ }
} else {
Type *comparison_type = x->type;
if (x->type == err_type && is_operand_nil(*x)) {
@@ -2926,11 +3001,11 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper
} else {
yt = type_to_string(y->type);
}
- err_str = gb_string_make(temporary_allocator(), gb_bprintf("mismatched types '%s' and '%s'", xt, yt));
+ err_str = gb_string_make(temporary_allocator(), gb_bprintf("Mismatched types '%s' and '%s'", xt, yt));
}
if (err_str != nullptr) {
- error(node, "Cannot compare expression, %s", err_str);
+ error(node, "Cannot compare expression. %s.", err_str);
x->type = t_untyped_bool;
} else {
if (x->mode == Addressing_Constant &&
@@ -2994,6 +3069,24 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper
case Token_LtEq: add_package_dependency(c, "runtime", "cstring_le"); break;
case Token_GtEq: add_package_dependency(c, "runtime", "cstring_gt"); break;
}
+ } else if (is_type_cstring16(x->type) && is_type_cstring16(y->type)) {
+ switch (op) {
+ case Token_CmpEq: add_package_dependency(c, "runtime", "cstring16_eq"); break;
+ case Token_NotEq: add_package_dependency(c, "runtime", "cstring16_ne"); break;
+ case Token_Lt: add_package_dependency(c, "runtime", "cstring16_lt"); break;
+ case Token_Gt: add_package_dependency(c, "runtime", "cstring16_gt"); break;
+ case Token_LtEq: add_package_dependency(c, "runtime", "cstring16_le"); break;
+ case Token_GtEq: add_package_dependency(c, "runtime", "cstring16_gt"); break;
+ }
+ } else if (is_type_string16(x->type) || is_type_string16(y->type)) {
+ switch (op) {
+ case Token_CmpEq: add_package_dependency(c, "runtime", "string16_eq"); break;
+ case Token_NotEq: add_package_dependency(c, "runtime", "string16_ne"); break;
+ case Token_Lt: add_package_dependency(c, "runtime", "string16_lt"); break;
+ case Token_Gt: add_package_dependency(c, "runtime", "string16_gt"); break;
+ case Token_LtEq: add_package_dependency(c, "runtime", "string16_le"); break;
+ case Token_GtEq: add_package_dependency(c, "runtime", "string16_gt"); break;
+ }
} else if (is_type_string(x->type) || is_type_string(y->type)) {
switch (op) {
case Token_CmpEq: add_package_dependency(c, "runtime", "string_eq"); break;
@@ -3045,126 +3138,106 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod
GB_ASSERT(node->kind == Ast_BinaryExpr);
ast_node(be, BinaryExpr, node);
- ExactValue x_val = {};
- if (x->mode == Addressing_Constant) {
- x_val = exact_value_to_integer(x->value);
+ bool y_is_untyped = is_type_untyped(y->type);
+ if (y_is_untyped) {
+ convert_to_typed(c, y, t_untyped_integer);
+ if (y->mode == Addressing_Invalid) {
+ x->mode = Addressing_Invalid;
+ return;
+ }
+ } else if (!is_type_unsigned(y->type)) {
+ gbString y_str = expr_to_string(y->expr);
+ error(y->expr, "Shift amount '%s' must be an unsigned integer", y_str);
+ gb_string_free(y_str);
+ x->mode = Addressing_Invalid;
+ return;
}
bool x_is_untyped = is_type_untyped(x->type);
- if (!(is_type_integer(x->type) || (x_is_untyped && x_val.kind == ExactValue_Integer))) {
- gbString err_str = expr_to_string(x->expr);
- error(node, "Shifted operand '%s' must be an integer", err_str);
- gb_string_free(err_str);
+ if (!(x_is_untyped || is_type_integer(x->type))) {
+ gbString x_str = expr_to_string(x->expr);
+ error(x->expr, "Shifted operand '%s' must be an integer", x_str);
+ gb_string_free(x_str);
x->mode = Addressing_Invalid;
return;
}
- if (is_type_unsigned(y->type)) {
-
- } else if (is_type_untyped(y->type)) {
- convert_to_typed(c, y, t_untyped_integer);
- if (y->mode == Addressing_Invalid) {
+ if (y->mode == Addressing_Constant) {
+ if (big_int_is_neg(&y->value.value_integer)) {
+ gbString y_str = expr_to_string(y->expr);
+ error(y->expr, "Shift amount '%s' cannot be negative", y_str);
+ gb_string_free(y_str);
x->mode = Addressing_Invalid;
return;
}
- } else {
- gbString err_str = expr_to_string(y->expr);
- error(node, "Shift amount '%s' must be an unsigned integer", err_str);
- gb_string_free(err_str);
- x->mode = Addressing_Invalid;
- return;
- }
+ BigInt max_shift = {};
+ big_int_from_u64(&max_shift, MAX_BIG_INT_SHIFT);
- if (x->mode == Addressing_Constant) {
- if (y->mode == Addressing_Constant) {
- ExactValue y_val = exact_value_to_integer(y->value);
- if (y_val.kind != ExactValue_Integer) {
- gbString err_str = expr_to_string(y->expr);
- error(node, "Shift amount '%s' must be an unsigned integer", err_str);
- gb_string_free(err_str);
- x->mode = Addressing_Invalid;
- return;
- }
+ if (big_int_cmp(&y->value.value_integer, &max_shift) > 0) {
+ gbString y_str = expr_to_string(y->expr);
+ error(y->expr, "Shift amount '%s' must be <= %u", y_str, MAX_BIG_INT_SHIFT);
+ gb_string_free(y_str);
+ x->mode = Addressing_Invalid;
+ return;
+ }
- BigInt max_shift = {};
- big_int_from_u64(&max_shift, MAX_BIG_INT_SHIFT);
+ if (x->mode == Addressing_Constant) {
+ if (x_is_untyped) {
+ convert_to_typed(c, x, t_untyped_integer);
+ if (x->mode == Addressing_Invalid) {
+ return;
+ }
- if (big_int_cmp(&y_val.value_integer, &max_shift) > 0) {
- gbString err_str = expr_to_string(y->expr);
- error(node, "Shift amount too large: '%s'", err_str);
- gb_string_free(err_str);
- x->mode = Addressing_Invalid;
- return;
- }
+ x->expr = node;
+ x->value = exact_value_shift(be->op.kind, exact_value_to_integer(x->value), exact_value_to_integer(y->value));
- if (!is_type_integer(x->type)) {
- // NOTE(bill): It could be an untyped float but still representable
- // as an integer
- x->type = t_untyped_integer;
+ return;
}
x->expr = node;
- x->value = exact_value_shift(be->op.kind, x_val, y_val);
+ x->value = exact_value_shift(be->op.kind, x->value, y->value);
+ check_is_expressible(c, x, x->type);
- if (is_type_typed(x->type)) {
- check_is_expressible(c, x, x->type);
- }
return;
}
- TokenPos pos = ast_token(x->expr).pos;
+ if (y_is_untyped) {
+ convert_to_typed(c, y, t_uint);
+ }
+
+ return;
+ }
+
+ if (x->mode == Addressing_Constant) {
if (x_is_untyped) {
- if (x->expr != nullptr) {
- x->expr->tav.is_lhs = true;
- }
- x->mode = Addressing_Value;
if (type_hint) {
if (is_type_integer(type_hint)) {
convert_to_typed(c, x, type_hint);
+ } else if (is_type_any(type_hint)) {
+ convert_to_typed(c, x, default_type(t_untyped_integer));
} else {
gbString x_str = expr_to_string(x->expr);
- gbString to_type = type_to_string(type_hint);
- error(node, "Conversion of shifted operand '%s' to '%s' is not allowed", x_str, to_type);
+ gbString type_str = type_to_string(type_hint);
+ error(x->expr, "Shifted operand '%s' cannot convert to non-integer type '%s'", x_str, type_str);
gb_string_free(x_str);
- gb_string_free(to_type);
+ gb_string_free(type_str);
x->mode = Addressing_Invalid;
+ return;
}
- } else if (!is_type_integer(x->type)) {
- gbString x_str = expr_to_string(x->expr);
- error(node, "Non-integer shifted operand '%s' is not allowed", x_str);
- gb_string_free(x_str);
- x->mode = Addressing_Invalid;
+ } else {
+ check_is_expressible(c, x, default_type(t_untyped_integer));
+ }
+ if (x->mode == Addressing_Invalid) {
+ return;
}
- // x->value = x_val;
- return;
}
- }
-
- if (y->mode == Addressing_Constant && big_int_is_neg(&y->value.value_integer)) {
- gbString err_str = expr_to_string(y->expr);
- error(node, "Shift amount cannot be negative: '%s'", err_str);
- gb_string_free(err_str);
- }
- if (!is_type_integer(x->type)) {
- gbString err_str = expr_to_string(x->expr);
- error(node, "Shift operand '%s' must be an integer", err_str);
- gb_string_free(err_str);
- x->mode = Addressing_Invalid;
- return;
- }
-
- if (is_type_untyped(y->type)) {
- convert_to_typed(c, y, t_uint);
+ x->mode = Addressing_Value;
}
-
- x->mode = Addressing_Value;
}
-
-
gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
if (check_is_assignable_to(c, operand, y)) {
return true;
@@ -3319,6 +3392,11 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
return true;
}
+ // []u16 <-> string16 (not cstring16)
+ if (is_type_u16_slice(src) && (is_type_string16(dst) && !is_type_cstring16(dst))) {
+ return true;
+ }
+
// cstring -> string
if (are_types_identical(src, t_cstring) && are_types_identical(dst, t_string)) {
if (operand->mode != Addressing_Constant) {
@@ -3326,6 +3404,14 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
}
return true;
}
+ // cstring16 -> string16
+ if (are_types_identical(src, t_cstring16) && are_types_identical(dst, t_string16)) {
+ if (operand->mode != Addressing_Constant) {
+ add_package_dependency(c, "runtime", "cstring16_to_string16");
+ }
+ return true;
+ }
+
// cstring -> ^u8
if (are_types_identical(src, t_cstring) && is_type_u8_ptr(dst)) {
return !is_constant;
@@ -3351,6 +3437,34 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
if (is_type_rawptr(src) && are_types_identical(dst, t_cstring)) {
return !is_constant;
}
+
+ // cstring -> ^u16
+ if (are_types_identical(src, t_cstring16) && is_type_u16_ptr(dst)) {
+ return !is_constant;
+ }
+ // cstring -> [^]u16
+ if (are_types_identical(src, t_cstring16) && is_type_u16_multi_ptr(dst)) {
+ return !is_constant;
+ }
+ // cstring16 -> rawptr
+ if (are_types_identical(src, t_cstring16) && is_type_rawptr(dst)) {
+ return !is_constant;
+ }
+
+
+ // ^u16 -> cstring16
+ if (is_type_u16_ptr(src) && are_types_identical(dst, t_cstring16)) {
+ return !is_constant;
+ }
+ // [^]u16 -> cstring
+ if (is_type_u16_multi_ptr(src) && are_types_identical(dst, t_cstring16)) {
+ return !is_constant;
+ }
+ // rawptr -> cstring16
+ if (is_type_rawptr(src) && are_types_identical(dst, t_cstring16)) {
+ return !is_constant;
+ }
+
// proc <-> proc
if (is_type_proc(src) && is_type_proc(dst)) {
if (is_type_polymorphic(dst)) {
@@ -3406,6 +3520,7 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
bool is_const_expr = x->mode == Addressing_Constant;
+
Type *bt = base_type(type);
if (is_const_expr && is_type_constant_type(bt)) {
if (core_type(bt)->kind == Type_Basic) {
@@ -3427,6 +3542,9 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type)
} else if (is_type_slice(type) && is_type_string(x->type)) {
x->mode = Addressing_Value;
} else if (is_type_union(type)) {
+ if (is_type_union_constantable(type)) {
+ return true;
+ }
x->mode = Addressing_Value;
}
if (x->mode == Addressing_Value) {
@@ -3666,6 +3784,11 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
}
gb_internal bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x, Operand *y) {
+ if (is_type_array_like(x->type) || is_type_array_like(y->type)) {
+ if (op.kind == Token_CmpAnd || op.kind == Token_CmpOr) {
+ error(op, "Array programming is not allowed with the operator '%.*s'", LIT(op.string));
+ }
+ }
if (is_type_array(x->type) && !is_type_array(y->type)) {
if (check_is_assignable_to(c, y, x->type)) {
if (check_binary_op(c, x, op)) {
@@ -3841,6 +3964,59 @@ matrix_error:
}
+gb_internal void check_binary_expr_dependency(CheckerContext *c, Token op, Type *bt, bool REQUIRE) {
+ if (op.kind == Token_Mod || op.kind == Token_ModEq ||
+ op.kind == Token_ModMod || op.kind == Token_ModModEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_u128: add_package_dependency(c, "runtime", "umodti3", REQUIRE); break;
+ case Basic_i128: add_package_dependency(c, "runtime", "modti3", REQUIRE); break;
+ }
+ } else if (op.kind == Token_Quo || op.kind == Token_QuoEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_complex32: add_package_dependency(c, "runtime", "quo_complex32"); break;
+ case Basic_complex64: add_package_dependency(c, "runtime", "quo_complex64"); break;
+ case Basic_complex128: add_package_dependency(c, "runtime", "quo_complex128"); break;
+ case Basic_quaternion64: add_package_dependency(c, "runtime", "quo_quaternion64"); break;
+ case Basic_quaternion128: add_package_dependency(c, "runtime", "quo_quaternion128"); break;
+ case Basic_quaternion256: add_package_dependency(c, "runtime", "quo_quaternion256"); break;
+
+ case Basic_u128: add_package_dependency(c, "runtime", "udivti3", REQUIRE); break;
+ case Basic_i128: add_package_dependency(c, "runtime", "divti3", REQUIRE); break;
+ }
+ } else if (op.kind == Token_Mul || op.kind == Token_MulEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_quaternion64: add_package_dependency(c, "runtime", "mul_quaternion64"); break;
+ case Basic_quaternion128: add_package_dependency(c, "runtime", "mul_quaternion128"); break;
+ case Basic_quaternion256: add_package_dependency(c, "runtime", "mul_quaternion256"); break;
+
+
+ case Basic_u128:
+ case Basic_i128:
+ if (is_arch_wasm()) {
+ add_package_dependency(c, "runtime", "__multi3", REQUIRE);
+ }
+ break;
+ }
+ } else if (op.kind == Token_Shl || op.kind == Token_ShlEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_u128:
+ case Basic_i128:
+ if (is_arch_wasm()) {
+ add_package_dependency(c, "runtime", "__ashlti3", REQUIRE);
+ }
+ break;
+ }
+ } else if (op.kind == Token_Shr || op.kind == Token_ShrEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_u128:
+ case Basic_i128:
+ if (is_arch_wasm()) {
+ add_package_dependency(c, "runtime", "__lshrti3", REQUIRE);
+ }
+ break;
+ }
+ }
+}
gb_internal 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);
@@ -4038,58 +4214,10 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
}
bool REQUIRE = true;
- Type *bt = base_type(x->type);
- if (op.kind == Token_Mod || op.kind == Token_ModEq ||
- op.kind == Token_ModMod || op.kind == Token_ModModEq) {
- if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_u128: add_package_dependency(c, "runtime", "umodti3", REQUIRE); break;
- case Basic_i128: add_package_dependency(c, "runtime", "modti3", REQUIRE); break;
- }
- } else if (op.kind == Token_Quo || op.kind == Token_QuoEq) {
- if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_complex32: add_package_dependency(c, "runtime", "quo_complex32"); break;
- case Basic_complex64: add_package_dependency(c, "runtime", "quo_complex64"); break;
- case Basic_complex128: add_package_dependency(c, "runtime", "quo_complex128"); break;
- case Basic_quaternion64: add_package_dependency(c, "runtime", "quo_quaternion64"); break;
- case Basic_quaternion128: add_package_dependency(c, "runtime", "quo_quaternion128"); break;
- case Basic_quaternion256: add_package_dependency(c, "runtime", "quo_quaternion256"); break;
-
- case Basic_u128: add_package_dependency(c, "runtime", "udivti3", REQUIRE); break;
- case Basic_i128: add_package_dependency(c, "runtime", "divti3", REQUIRE); break;
- }
- } else if (op.kind == Token_Mul || op.kind == Token_MulEq) {
- if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_quaternion64: add_package_dependency(c, "runtime", "mul_quaternion64"); break;
- case Basic_quaternion128: add_package_dependency(c, "runtime", "mul_quaternion128"); break;
- case Basic_quaternion256: add_package_dependency(c, "runtime", "mul_quaternion256"); break;
-
-
- case Basic_u128:
- case Basic_i128:
- if (is_arch_wasm()) {
- add_package_dependency(c, "runtime", "__multi3", REQUIRE);
- }
- break;
- }
- } else if (op.kind == Token_Shl || op.kind == Token_ShlEq) {
- if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_u128:
- case Basic_i128:
- if (is_arch_wasm()) {
- add_package_dependency(c, "runtime", "__ashlti3", REQUIRE);
- }
- break;
- }
- } else if (op.kind == Token_Shr || op.kind == Token_ShrEq) {
- if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_u128:
- case Basic_i128:
- if (is_arch_wasm()) {
- add_package_dependency(c, "runtime", "__lshrti3", REQUIRE);
- }
- break;
- }
- }
+ Type *btx = base_type(x->type);
+ Type *bty = base_type(y->type);
+ check_binary_expr_dependency(c, op, btx, REQUIRE);
+ check_binary_expr_dependency(c, op, bty, REQUIRE);
if (token_is_shift(op.kind)) {
check_shift(c, x, y, node, type_hint);
@@ -4204,7 +4332,25 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
}
if (fail) {
- error(y->expr, "Division by zero not allowed");
+ if (is_type_integer(x->type) || (x->mode == Addressing_Constant && x->value.kind == ExactValue_Integer)) {
+ if (check_for_integer_division_by_zero(c, node) != IntegerDivisionByZero_Trap) {
+ // Okay
+ break;
+ }
+ }
+
+ switch (op.kind) {
+ case Token_Mod:
+ case Token_ModMod:
+ case Token_ModEq:
+ case Token_ModModEq:
+ error(y->expr, "Division by zero through '%.*s' not allowed", LIT(token_strings[op.kind]));
+ break;
+ case Token_Quo:
+ case Token_QuoEq:
+ error(y->expr, "Division by zero not allowed");
+ break;
+ }
x->mode = Addressing_Invalid;
return;
}
@@ -4244,7 +4390,59 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
}
}
- x->value = exact_binary_operator_value(op.kind, a, b);
+ match_exact_values(&a, &b);
+
+
+ IntegerDivisionByZeroKind zero_behaviour = check_for_integer_division_by_zero(c, node);
+ if (zero_behaviour != IntegerDivisionByZero_Trap &&
+ b.kind == ExactValue_Integer && big_int_is_zero(&b.value_integer) &&
+ (op.kind == Token_QuoEq || op.kind == Token_Mod || op.kind == Token_ModMod)) {
+ if (op.kind == Token_QuoEq) {
+ switch (zero_behaviour) {
+ case IntegerDivisionByZero_Zero:
+ // x/0 == 0
+ x->value = b;
+ break;
+ case IntegerDivisionByZero_Self:
+ // x/0 == x
+ x->value = a;
+ break;
+ case IntegerDivisionByZero_AllBits:
+ // x/0 == 0b111...111
+ if (is_type_untyped(x->type)) {
+ x->value = exact_value_i64(-1);
+ } else {
+ x->value = exact_unary_operator_value(Token_Xor, b, cast(i32)(8*type_size_of(x->type)), is_type_unsigned(x->type));
+ }
+ break;
+ }
+ } else {
+ /*
+ NOTE(bill): @integer division by zero rules
+
+ truncated: r = a - b*trunc(a/b)
+ floored: r = a - b*floor(a/b)
+
+ IFF a/0 == 0, then (a%0 == a) or (a%%0 == a)
+ IFF a/0 == a, then (a%0 == 0) or (a%%0 == 0)
+ IFF a/0 == 0b111..., then (a%0 == a) or (a%%0 == a)
+ */
+
+ switch (zero_behaviour) {
+ case IntegerDivisionByZero_Zero:
+ case IntegerDivisionByZero_AllBits:
+ // x%0 == x
+ x->value = a;
+ break;
+ case IntegerDivisionByZero_Self:
+ // x%0 == 0
+ x->value = b;
+ break;
+ }
+ }
+ } else {
+ x->value = exact_binary_operator_value(op.kind, a, b);
+ }
if (is_type_typed(x->type)) {
if (node != nullptr) {
@@ -4462,7 +4660,6 @@ gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
}
gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
- // GB_ASSERT_NOT_NULL(target_type);
if (target_type == nullptr || operand->mode == Addressing_Invalid ||
operand->mode == Addressing_Type ||
is_type_typed(operand->type) ||
@@ -4504,8 +4701,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
} else {
switch (operand->type->Basic.kind) {
case Basic_UntypedBool:
- if (!is_type_boolean(target_type) &&
- !is_type_integer(target_type)) {
+ if (!is_type_boolean(target_type)) {
operand->mode = Addressing_Invalid;
convert_untyped_error(c, operand, target_type);
return;
@@ -4528,6 +4724,8 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
// target_type = t_untyped_nil;
} else if (is_type_cstring(target_type)) {
// target_type = t_untyped_nil;
+ } else if (is_type_cstring16(target_type)) {
+ // target_type = t_untyped_nil;
} else if (!type_has_nil(target_type)) {
operand->mode = Addressing_Invalid;
convert_untyped_error(c, operand, target_type);
@@ -4555,6 +4753,13 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
break;
}
}
+ } else if (operand->value.kind == ExactValue_String16) {
+ String16 s = operand->value.value_string16;
+ if (is_type_u16_array(t)) {
+ if (s.len == t->Array.count) {
+ break;
+ }
+ }
}
operand->mode = Addressing_Invalid;
convert_untyped_error(c, operand, target_type);
@@ -4625,7 +4830,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
TEMPORARY_ALLOCATOR_GUARD();
isize count = t->Union.variants.count;
- ValidIndexAndScore *valids = gb_alloc_array(temporary_allocator(), ValidIndexAndScore, count);
+ ValidIndexAndScore *valids = temporary_alloc_array<ValidIndexAndScore>(count);
isize valid_count = 0;
isize first_success_index = -1;
for_array(i, t->Union.variants) {
@@ -4666,7 +4871,10 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
break;
}
operand->type = new_type;
- operand->mode = Addressing_Value;
+ if (operand->mode != Addressing_Constant ||
+ !elem_type_can_be_constant(operand->type)) {
+ operand->mode = Addressing_Value;
+ }
break;
} else if (valid_count > 1) {
ERROR_BLOCK();
@@ -4884,6 +5092,12 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
if (success_) *success_ = true;
if (finish_) *finish_ = true;
return exact_value_u64(val);
+ } else if (value.kind == ExactValue_String16) {
+ GB_ASSERT(0 <= index && index < value.value_string.len);
+ u16 val = value.value_string16[index];
+ if (success_) *success_ = true;
+ if (finish_) *finish_ = true;
+ return exact_value_u64(val);
}
if (value.kind != ExactValue_Compound) {
if (success_) *success_ = true;
@@ -4902,7 +5116,11 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
}
if (cl->elems[0]->kind == Ast_FieldValue) {
- if (is_type_struct(node->tav.type)) {
+ if (is_type_raw_union(node->tav.type)) {
+ if (success_) *success_ = false;
+ if (finish_) *finish_ = true;
+ return empty_exact_value;
+ } else if (is_type_struct(node->tav.type)) {
bool found = false;
for (Ast *elem : cl->elems) {
if (elem->kind != Ast_FieldValue) {
@@ -4911,7 +5129,6 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
ast_node(fv, FieldValue, elem);
String name = fv->field->Ident.token.string;
Selection sub_sel = lookup_field(node->tav.type, name, false);
- defer (array_free(&sub_sel.index));
if (sub_sel.index.count > 0 &&
sub_sel.index[0] == index) {
value = fv->value->tav.value;
@@ -5172,7 +5389,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *
}
} else */if (node->kind == Ast_Ident) {
String name = node->Ident.token.string;
- return scope_lookup(c->scope, name);
+ return scope_lookup(c->scope, name, node->Ident.hash);
} else if (!ident_only) if (node->kind == Ast_SelectorExpr) {
ast_node(se, SelectorExpr, node);
if (se->token.kind == Token_ArrowRight) {
@@ -5194,7 +5411,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *
if (op_expr->kind == Ast_Ident) {
String op_name = op_expr->Ident.token.string;
- Entity *e = scope_lookup(c->scope, op_name);
+ Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash);
if (e == nullptr) {
return nullptr;
}
@@ -5291,7 +5508,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
if (op_expr->kind == Ast_Ident) {
String op_name = op_expr->Ident.token.string;
- Entity *e = scope_lookup(c->scope, op_name);
+ Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash);
add_entity_use(c, op_expr, e);
expr_entity = e;
@@ -5331,16 +5548,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
return nullptr;
}
- check_entity_decl(c, entity, nullptr, nullptr);
- if (entity->kind == Entity_ProcGroup) {
- operand->mode = Addressing_ProcGroup;
- operand->proc_group = entity;
-
- add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
- return entity;
- }
- GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind]));
-
if (!is_entity_exported(entity, allow_builtin)) {
gbString sel_str = expr_to_string(selector);
error(node, "'%s' is not exported by '%.*s'", sel_str, LIT(import_name));
@@ -5351,36 +5558,15 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
// return nullptr;
}
+ check_entity_decl(c, entity, nullptr, nullptr);
if (entity->kind == Entity_ProcGroup) {
- Array<Entity *> procs = entity->ProcGroup.entities;
- bool skip = false;
- for (Entity *p : procs) {
- Type *t = base_type(p->type);
- if (t == t_invalid) {
- continue;
- }
-
- Operand x = {};
- x.mode = Addressing_Value;
- x.type = t;
- if (type_hint != nullptr) {
- if (check_is_assignable_to(c, &x, type_hint)) {
- entity = p;
- skip = true;
- break;
- }
- }
- }
+ operand->mode = Addressing_ProcGroup;
+ operand->proc_group = entity;
- if (!skip) {
- GB_ASSERT(entity != nullptr);
- operand->mode = Addressing_ProcGroup;
- operand->type = t_invalid;
- operand->expr = node;
- operand->proc_group = entity;
- return entity;
- }
+ add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
+ return entity;
}
+ GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind]));
}
}
@@ -5419,8 +5605,18 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
}
}
+ if (operand->type && is_type_simd_vector(type_deref(operand->type))) {
+ String field_name = selector->Ident.token.string;
+ if (field_name.len == 1) {
+ error(op_expr, "Extracting an element from a #simd array using .%.*s syntax is disallowed, prefer `simd.extract`", LIT(field_name));
+ } else {
+ error(op_expr, "Extracting elements from a #simd array using .%.*s syntax is disallowed, prefer `swizzle`", LIT(field_name));
+ }
+ return nullptr;
+ }
+
if (entity == nullptr && selector->kind == Ast_Ident && operand->type != nullptr &&
- (is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) {
+ (is_type_array(type_deref(operand->type)))) {
String field_name = selector->Ident.token.string;
if (1 < field_name.len && field_name.len <= 4) {
u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'};
@@ -5475,7 +5671,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Type *original_type = operand->type;
Type *array_type = base_type(type_deref(original_type));
- GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
+ GB_ASSERT(array_type->kind == Type_Array);
i64 array_count = get_array_type_count(array_type);
@@ -5516,10 +5712,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
break;
}
- if (array_type->kind == Type_SimdVector) {
- operand->mode = Addressing_Value;
- }
-
Entity *swizzle_entity = alloc_entity_variable(nullptr, make_token_ident(field_name), operand->type, EntityState_Resolved);
add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
return swizzle_entity;
@@ -5642,7 +5834,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
switch (entity->kind) {
case Entity_Constant:
- operand->value = entity->Constant.value;
+ operand->value = entity->Constant.value;
operand->mode = Addressing_Constant;
if (operand->value.kind == ExactValue_Procedure) {
Entity *proc = strip_entity_wrapping(operand->value.value_procedure);
@@ -5739,7 +5931,7 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals
return true;
}
} else {
- Entity *e = scope_lookup(s, name);
+ Entity *e = scope_lookup(s, name, i->hash);
if (e != nullptr) {
if (out_scope) *out_scope = e->scope;
return true;
@@ -5758,22 +5950,6 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals
return false;
}
-gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) {
- if (o.type && is_type_no_copy(o.type)) {
- Ast *expr = unparen_expr(o.expr);
- if (expr && o.mode != Addressing_Constant && o.mode != Addressing_Type) {
- if (expr->kind == Ast_CallExpr) {
- // Okay
- } else {
- error(o.expr, "Invalid use of #no_copy value in %.*s", LIT(context));
- return true;
- }
- }
- }
- return false;
-}
-
-
gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs, Array<Operand> *operands, Slice<Ast *> const &rhs) {
bool optional_ok = false;
isize tuple_index = 0;
@@ -5844,7 +6020,6 @@ gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array<Operand>
for (Entity *e : tuple->variables) {
o.type = e->type;
array_add(operands, o);
- check_no_copy_assignment(o, str_lit("assignment"));
}
tuple_index += tuple->variables.count;
@@ -5859,12 +6034,12 @@ typedef u32 UnpackFlags;
enum UnpackFlag : u32 {
UnpackFlag_None = 0,
UnpackFlag_AllowOk = 1<<0,
- UnpackFlag_IsVariadic = 1<<1,
- UnpackFlag_AllowUndef = 1<<2,
+ UnpackFlag_AllowUndef = 1<<1,
};
-gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags) {
+gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags,
+ isize variadic_index = -1) {
auto const &add_dependencies_from_unpacking = [](CheckerContext *c, Entity **lhs, isize lhs_count, isize tuple_index, isize tuple_count) -> isize {
if (lhs == nullptr || c->decl == nullptr) {
return tuple_count;
@@ -5880,7 +6055,7 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
}
rw_mutex_shared_lock(&decl->deps_mutex);
rw_mutex_lock(&c->decl->deps_mutex);
- for (Entity *dep : decl->deps) {
+ FOR_PTR_SET(dep, decl->deps) {
ptr_set_add(&c->decl->deps, dep);
}
rw_mutex_unlock(&c->decl->deps_mutex);
@@ -5889,11 +6064,14 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
return tuple_count;
};
-
bool allow_ok = (flags & UnpackFlag_AllowOk) != 0;
- bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0;
bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0;
+ bool is_variadic = variadic_index > -1;
+ if (!is_variadic) {
+ variadic_index = lhs_count;
+ }
+
bool optional_ok = false;
isize tuple_index = 0;
for (Ast *rhs : rhs_arguments) {
@@ -5909,26 +6087,18 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
Type *type_hint = nullptr;
-
- if (lhs != nullptr && tuple_index < lhs_count) {
- // NOTE(bill): override DeclInfo for dependency
- Entity *e = lhs[tuple_index];
- if (e != nullptr) {
- type_hint = e->type;
- if (e->flags & EntityFlag_Ellipsis) {
- GB_ASSERT(is_type_slice(e->type));
- GB_ASSERT(e->type->kind == Type_Slice);
- type_hint = e->type->Slice.elem;
+ if (lhs != nullptr) {
+ if (tuple_index < variadic_index) {
+ // NOTE(bill): override DeclInfo for dependency
+ Entity *e = lhs[tuple_index];
+ if (e != nullptr) {
+ type_hint = e->type;
}
- }
- } else if (lhs != nullptr && tuple_index >= lhs_count && is_variadic) {
- // NOTE(bill): override DeclInfo for dependency
- Entity *e = lhs[lhs_count-1];
- if (e != nullptr) {
- type_hint = e->type;
- if (e->flags & EntityFlag_Ellipsis) {
+ } else if (is_variadic) {
+ Entity *e = lhs[variadic_index];
+ if (e != nullptr) {
+ GB_ASSERT(e->flags & EntityFlag_Ellipsis);
GB_ASSERT(is_type_slice(e->type));
- GB_ASSERT(e->type->kind == Type_Slice);
type_hint = e->type->Slice.elem;
}
}
@@ -6075,7 +6245,8 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
Entity *entity, Type *proc_type,
Array<Operand> positional_operands, Array<Operand> const &named_operands,
CallArgumentErrorMode show_error_mode,
- CallArgumentData *data) {
+ CallArgumentData *data,
+ bool checking_proc_group) {
TEMPORARY_ALLOCATOR_GUARD();
CallArgumentError err = CallArgumentError_None;
@@ -6112,7 +6283,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
GB_ASSERT(ce->split_args);
- auto visited = slice_make<bool>(temporary_allocator(), pt->param_count);
+ auto visited = temporary_slice_make<bool>(pt->param_count);
auto ordered_operands = array_make<Operand>(temporary_allocator(), pt->param_count);
defer ({
for (Operand const &o : ordered_operands) {
@@ -6236,29 +6407,46 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
- for (Operand const &o : ordered_operands) {
- if (o.mode != Addressing_Invalid) {
- check_no_copy_assignment(o, str_lit("procedure call expression"));
- }
- }
-
for (isize i = 0; i < pt->param_count; i++) {
if (!visited[i]) {
Entity *e = pt->params->Tuple.variables[i];
+ bool context_allocator_error = false;
if (e->kind == Entity_Variable) {
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
- ordered_operands[i].mode = Addressing_Value;
- ordered_operands[i].type = e->type;
- ordered_operands[i].expr = e->Variable.param_value.original_ast_expr;
+ if (ast_file_vet_explicit_allocators(c->file) && !checking_proc_group) {
+ // NOTE(lucas): check if we are trying to default to context.allocator or context.temp_allocator
+ if (e->Variable.param_value.original_ast_expr->kind == Ast_SelectorExpr) {
+ auto& expr = e->Variable.param_value.original_ast_expr->SelectorExpr.expr;
+ auto& selector = e->Variable.param_value.original_ast_expr->SelectorExpr.selector;
+ if (expr->kind == Ast_Implicit &&
+ expr->Implicit.string == STR_LIT("context") &&
+ selector->kind == Ast_Ident &&
+ (selector->Ident.token.string == STR_LIT("allocator") ||
+ selector->Ident.token.string == STR_LIT("temp_allocator"))) {
+ context_allocator_error = true;
+ }
+ }
+ }
- dummy_argument_count += 1;
- score += assign_score_function(1);
- continue;
+ if (!context_allocator_error) {
+ ordered_operands[i].mode = Addressing_Value;
+ ordered_operands[i].type = e->type;
+ ordered_operands[i].expr = e->Variable.param_value.original_ast_expr;
+
+ dummy_argument_count += 1;
+ score += assign_score_function(1);
+ continue;
+ }
}
}
if (show_error) {
- if (e->kind == Entity_TypeName) {
+ if (context_allocator_error) {
+ gbString str = type_to_string(e->type);
+ error(call, "Parameter '%.*s' of type '%s' must be explicitly provided in procedure call",
+ LIT(e->token.string), str);
+ gb_string_free(str);
+ } else if (e->kind == Entity_TypeName) {
error(call, "Type parameter '%.*s' is missing in procedure call",
LIT(e->token.string));
} else if (e->kind == Entity_Constant && e->Constant.value.kind != ExactValue_Invalid) {
@@ -6310,6 +6498,21 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
}
+ if (e && e->kind == Entity_Constant && is_type_proc(e->type)) {
+ bool ok = false;
+ if (o->mode == Addressing_Constant) {
+ ok = true;
+ } else if (o->value.kind == ExactValue_Procedure) {
+ ok = true;
+ }
+
+ if (!ok) {
+ if (show_error) {
+ error(o->expr, "Expected a constant procedure value for the argument '%.*s'", LIT(e->token.string));
+ }
+ err = CallArgumentError_NoneConstantParameter;
+ }
+ }
if (!err && is_type_any(param_type)) {
add_type_info_type(c, o->type);
@@ -6468,17 +6671,16 @@ gb_internal bool is_call_expr_field_value(AstCallExpr *ce) {
return ce->args[0]->kind == Ast_FieldValue;
}
-gb_internal Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize *lhs_count_, bool *is_variadic) {
+gb_internal Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize *lhs_count_) {
Entity **lhs = nullptr;
isize lhs_count = -1;
- if (proc_type == nullptr) {
+ if (proc_type == nullptr || proc_type == t_invalid) {
return nullptr;
}
GB_ASSERT(is_type_proc(proc_type));
TypeProc *pt = &base_type(proc_type)->Proc;
- *is_variadic = pt->variadic;
if (!pt->is_polymorphic || pt->is_poly_specialized) {
if (pt->params != nullptr) {
@@ -6534,7 +6736,7 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco
Entity *e = entry.value;
switch (e->kind) {
case Entity_TypeName: {
- if (print_count == 0) error_line("\n\tWith the following definitions:\n");
+ // 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);
@@ -6653,7 +6855,8 @@ gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Opera
Entity *e, Type *proc_type,
Array<Operand> const &positional_operands, Array<Operand> const &named_operands,
CallArgumentErrorMode show_error_mode,
- CallArgumentData *data) {
+ CallArgumentData *data,
+ bool checking_proc_group) {
bool return_on_failure = show_error_mode == CallArgumentErrorMode::NoErrors;
@@ -6672,9 +6875,12 @@ gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Opera
GB_ASSERT(proc_type != nullptr);
proc_type = base_type(proc_type);
+ if (proc_type == t_invalid) {
+ return false;
+ }
GB_ASSERT(proc_type->kind == Type_Proc);
- CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data);
+ CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data, checking_proc_group);
if (return_on_failure && err != CallArgumentError_None) {
return false;
}
@@ -6799,31 +7005,40 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
array_unordered_remove(&procs, proc_index);
continue;
}
+ if (!pt->Proc.variadic && max_arg_count != ISIZE_MAX && param_count < max_arg_count) {
+ array_unordered_remove(&procs, proc_index);
+ continue;
+ }
proc_index++;
}
}
Entity **lhs = nullptr;
isize lhs_count = -1;
- bool is_variadic = false;
+ i32 variadic_index = -1;
- auto positional_operands = array_make<Operand>(heap_allocator(), 0, 0);
- auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
- defer (array_free(&positional_operands));
- defer (array_free(&named_operands));
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ auto positional_operands = array_make<Operand>(temporary_allocator(), 0, 0);
+ auto named_operands = array_make<Operand>(temporary_allocator(), 0, 0);
if (procs.count == 1) {
Entity *e = procs[0];
-
- lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
- check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
+ Type *pt = base_type(e->type);
+ if (pt != nullptr && is_type_proc(pt)) {
+ lhs = populate_proc_parameter_list(c, pt, &lhs_count);
+ if (pt->Proc.variadic) {
+ variadic_index = pt->Proc.variadic_index;
+ }
+ }
+ check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, UnpackFlag_None, variadic_index);
if (check_named_arguments(c, e->type, named_args, &named_operands, true)) {
check_call_arguments_single(c, call, operand,
e, e->type,
positional_operands, named_operands,
CallArgumentErrorMode::ShowErrors,
- &data);
+ &data, false);
}
return data;
}
@@ -6846,7 +7061,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
if (proc_arg_count >= 0) {
lhs_count = proc_arg_count;
if (lhs_count > 0) {
- lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count);
+ lhs = gb_alloc_array(temporary_allocator(), Entity *, lhs_count);
for (isize param_index = 0; param_index < lhs_count; param_index++) {
Entity *e = nullptr;
for (Entity *p : procs) {
@@ -6876,11 +7091,30 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
}
lhs[param_index] = e;
}
+
+ for (Entity *p : procs) {
+ Type *pt = base_type(p->type);
+ if (!(pt != nullptr && is_type_proc(pt))) {
+ continue;
+ }
+
+ if (pt->Proc.is_polymorphic) {
+ if (variadic_index == -1) {
+ variadic_index = pt->Proc.variadic_index;
+ } else if (variadic_index != pt->Proc.variadic_index) {
+ variadic_index = -1;
+ break;
+ }
+ } else {
+ variadic_index = -1;
+ break;
+ }
+ }
}
}
}
- check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
+ check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, UnpackFlag_None, variadic_index);
for_array(i, named_args) {
Ast *arg = named_args[i];
@@ -6913,13 +7147,9 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
array_add(&named_operands, o);
}
- gb_free(heap_allocator(), lhs);
-
- auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
- defer (array_free(&valids));
+ auto valids = array_make<ValidIndexAndScore>(temporary_allocator(), 0, procs.count);
- auto proc_entities = array_make<Entity *>(heap_allocator(), 0, procs.count*2 + 1);
- defer (array_free(&proc_entities));
+ auto proc_entities = array_make<Entity *>(temporary_allocator(), 0, procs.count*2 + 1);
for (Entity *proc : procs) {
array_add(&proc_entities, proc);
}
@@ -6929,6 +7159,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
gbString expr_name = expr_to_string(operand->expr);
defer (gb_string_free(expr_name));
+ c->in_proc_group = true;
for_array(i, procs) {
Entity *p = procs[i];
if (p->flags & EntityFlag_Disabled) {
@@ -6948,7 +7179,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
p, pt,
positional_operands, named_operands,
CallArgumentErrorMode::NoErrors,
- &data);
+ &data, true);
if (!is_a_candidate) {
continue;
}
@@ -6971,6 +7202,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
array_add(&valids, item);
}
}
+ c->in_proc_group = false;
if (max_matched_features > 0) {
for_array(i, valids) {
@@ -7048,7 +7280,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
}
// Try to reduce the list further for `$T: typeid` like parameters
- bool *possibly_ignore = gb_alloc_array(temporary_allocator(), bool, procs.count);
+ bool *possibly_ignore = temporary_alloc_array<bool>(procs.count);
isize possibly_ignore_set = 0;
if (true) {
@@ -7136,7 +7368,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
}
isize max_spaces = gb_max(max_name_length, max_type_length);
- char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1);
+ char *spaces = temporary_alloc_array<char>(max_spaces+1);
for (isize i = 0; i < max_spaces; i++) {
spaces[i] = ' ';
}
@@ -7257,7 +7489,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
e, e->type,
positional_operands, named_operands,
CallArgumentErrorMode::ShowErrors,
- &data);
+ &data, false);
return data;
}
@@ -7311,20 +7543,22 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
return check_call_arguments_proc_group(c, operand, call);
}
- auto positional_operands = array_make<Operand>(heap_allocator(), 0, positional_args.count);
- auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
+ TEMPORARY_ALLOCATOR_GUARD();
- defer (array_free(&positional_operands));
- defer (array_free(&named_operands));
+ auto positional_operands = array_make<Operand>(temporary_allocator(), 0, positional_args.count);
+ auto named_operands = array_make<Operand>(temporary_allocator(), 0, 0);
if (positional_args.count > 0) {
- isize lhs_count = -1;
- bool is_variadic = false;
Entity **lhs = nullptr;
+ isize lhs_count = -1;
+ i32 variadic_index = -1;
if (pt != nullptr) {
- lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic);
+ lhs = populate_proc_parameter_list(c, proc_type, &lhs_count);
+ if (pt->variadic) {
+ variadic_index = pt->variadic_index;
+ }
}
- check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
+ check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, UnpackFlag_None, variadic_index);
}
if (named_args.count > 0) {
@@ -7367,7 +7601,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
nullptr, proc_type,
positional_operands, named_operands,
CallArgumentErrorMode::ShowErrors,
- &data);
+ &data, false);
} else if (pt) {
data.result_type = pt->results;
}
@@ -7399,6 +7633,8 @@ gb_internal isize lookup_polymorphic_record_parameter(Type *t, String parameter_
gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *operand, Ast *call) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
ast_node(ce, CallExpr, call);
Type *original_type = operand->type;
@@ -7407,7 +7643,6 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
bool show_error = true;
Array<Operand> operands = {};
- defer (array_free(&operands));
CallArgumentError err = CallArgumentError_None;
@@ -7415,15 +7650,14 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
{
// 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;
- });
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ c->type_path = new_checker_type_path(temporary_allocator());
+ defer (c->type_path = prev_type_path);
if (is_call_expr_field_value(ce)) {
named_fields = true;
- operands = array_make<Operand>(heap_allocator(), ce->args.count);
+ operands = array_make<Operand>(temporary_allocator(), ce->args.count);
for_array(i, ce->args) {
Ast *arg = ce->args[i];
ast_node(fv, FieldValue, arg);
@@ -7455,7 +7689,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
}
} else {
- operands = array_make<Operand>(heap_allocator(), 0, 2*ce->args.count);
+ operands = array_make<Operand>(temporary_allocator(), 0, 2*ce->args.count);
Entity **lhs = nullptr;
isize lhs_count = -1;
@@ -7497,7 +7731,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
} else {
TEMPORARY_ALLOCATOR_GUARD();
- bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count);
+ bool *visited = temporary_alloc_array<bool>(param_count);
// LEAK(bill)
ordered_operands = array_make<Operand>(permanent_allocator(), param_count);
@@ -7681,11 +7915,10 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
{
GenTypesData *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(c, original_type);
-
mutex_lock(&found_gen_types->mutex);
defer (mutex_unlock(&found_gen_types->mutex));
- Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands);
+ Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands);
if (found_entity) {
operand->mode = Addressing_Type;
operand->type = found_entity->type;
@@ -7745,7 +7978,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
s = gb_string_append_fmt(s, "$%.*s", LIT(name));
if (v->kind == Entity_TypeName) {
- if (v->type->kind != Type_Generic) {
+ if (v->type != nullptr && v->type->kind != Type_Generic) {
s = gb_string_append_fmt(s, "=");
s = write_type_to_string(s, v->type, false);
}
@@ -7813,6 +8046,173 @@ gb_internal bool check_call_parameter_mixture(Slice<Ast *> const &args, char con
return Expr_Stmt; \
}
+gb_internal ExprKind check_call_expr_as_type_cast(CheckerContext *c, Operand *operand, Ast *call, Slice<Ast *> const &args, Type *type_hint) {
+ GB_ASSERT(operand->mode == Addressing_Type);
+ Type *t = operand->type;
+ if (is_type_polymorphic_record(t)) {
+ CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction");
+
+ if (!is_type_named(t)) {
+ gbString s = expr_to_string(operand->expr);
+ error(call, "Illegal use of an unnamed polymorphic record, %s", s);
+ gb_string_free(s);
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;;
+ return Expr_Expr;
+ }
+ auto err = check_polymorphic_record_type(c, operand, call);
+ if (err == 0) {
+ Ast *ident = operand->expr;
+ while (ident->kind == Ast_SelectorExpr) {
+ Ast *s = ident->SelectorExpr.selector;
+ ident = s;
+ }
+ 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, call, Addressing_Type, ot, empty_exact_value);
+ } else {
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ }
+ } else {
+ CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion");
+
+ operand->mode = Addressing_Invalid;
+ isize arg_count = args.count;
+ switch (arg_count) {
+ case 0:
+ {
+ gbString str = type_to_string(t);
+ error(call, "Missing argument in conversion to '%s'", str);
+ gb_string_free(str);
+ } break;
+ default:
+ {
+ gbString str = type_to_string(t);
+ if (t->kind == Type_Basic) {
+ ERROR_BLOCK();
+ switch (t->Basic.kind) {
+ case Basic_complex32:
+ case Basic_complex64:
+ case Basic_complex128:
+ error(call, "Too many arguments in conversion to '%s'", str);
+ error_line("\tSuggestion: %s(1+2i) or construct with 'complex'\n", str);
+ break;
+ case Basic_quaternion64:
+ case Basic_quaternion128:
+ case Basic_quaternion256:
+ error(call, "Too many arguments in conversion to '%s'", str);
+ error_line("\tSuggestion: %s(1+2i+3j+4k) or construct with 'quaternion'\n", str);
+ break;
+ default:
+ error(call, "Too many arguments in conversion to '%s'", str);
+ }
+ } else {
+ error(call, "Too many arguments in conversion to '%s'", str);
+ }
+ gb_string_free(str);
+ } break;
+ case 1: {
+ Ast *arg = args[0];
+ if (arg->kind == Ast_FieldValue) {
+ error(call, "'field = value' cannot be used in a type conversion");
+ arg = arg->FieldValue.value;
+ // NOTE(bill): Carry on the cast regardless
+ }
+ check_expr_with_type_hint(c, operand, arg, t);
+ if (operand->mode != Addressing_Invalid) {
+ if (is_type_polymorphic(t)) {
+ error(call, "A polymorphic type cannot be used in a type conversion");
+ } else {
+ // NOTE(bill): Otherwise the compiler can override the polymorphic type
+ // as it assumes it is determining the type
+ check_cast(c, operand, t);
+ }
+ }
+ operand->type = t;
+ operand->expr = call;
+
+
+ if (operand->mode != Addressing_Invalid) {
+ update_untyped_expr_type(c, arg, t, false);
+ }
+ break;
+ }
+ }
+ }
+ return Expr_Expr;
+}
+
+
+void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types);
+
+gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast *call, Entity *proc_entity, Type *proc_type) {
+ auto &proc = proc_type->Proc;
+ Slice<Entity *> params = proc.params ? proc.params->Tuple.variables : Slice<Entity *>{};
+
+ Type *self_type = nullptr;
+ isize params_start = 1;
+
+ ast_node(ce, CallExpr, call);
+
+ Type *return_type = proc.result_count == 0 ? nullptr : proc.results->Tuple.variables[0]->type;
+ bool is_return_instancetype = return_type != nullptr && return_type == t_objc_instancetype;
+
+ if (params.count == 0 || !is_type_objc_ptr_to_object(params[0]->type)) {
+ if (!proc_entity->Procedure.is_objc_class_method) {
+ // Not a class method, invalid call
+ error(call, "Invalid Objective-C call: The Objective-C method is not a class method but this first parameter is not an Objective-C object pointer.");
+ return;
+ }
+
+ if (is_return_instancetype) {
+ if (ce->proc->kind == Ast_SelectorExpr) {
+ ast_node(se, SelectorExpr, ce->proc);
+
+ // NOTE(harold): These should have already been checked, right?
+ GB_ASSERT(se->expr->tav.mode == Addressing_Type && se->expr->tav.type->kind == Type_Named);
+
+ return_type = alloc_type_pointer(se->expr->tav.type);
+ } else {
+ return_type = proc_entity->Procedure.objc_class->type;
+ }
+ }
+
+ self_type = t_objc_Class;
+ params_start = 0;
+ } else if (ce->args.count > 0) {
+ GB_ASSERT(is_type_objc_ptr_to_object(params[0]->type));
+
+ if (ce->args[0]->tav.objc_super_target) {
+ self_type = t_objc_super_ptr;
+ } else {
+ self_type = ce->args[0]->tav.type;
+ }
+
+ if (is_return_instancetype) {
+ // NOTE(harold): These should have already been checked, right?
+ GB_ASSERT(ce->args[0]->tav.type && ce->args[0]->tav.type->kind == Type_Pointer && ce->args[0]->tav.type->Pointer.elem->kind == Type_Named);
+
+ return_type = ce->args[0]->tav.type;
+ }
+ }
+
+ auto param_types = slice_make<Type *>(permanent_allocator(), proc.param_count + 2 - params_start);
+ param_types[0] = self_type;
+ param_types[1] = t_objc_SEL;
+
+ for (isize i = params_start; i < params.count; i++) {
+ param_types[i+2-params_start] = params[i]->type;
+ }
+
+ if (is_return_instancetype) {
+ operand->type = return_type;
+ }
+
+ add_objc_proc_type(c, call, return_type, param_types);
+}
gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
if (proc != nullptr &&
@@ -7869,79 +8269,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
if (operand->mode == Addressing_Type) {
- Type *t = operand->type;
- if (is_type_polymorphic_record(t)) {
- CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction");
-
- if (!is_type_named(t)) {
- gbString s = expr_to_string(operand->expr);
- error(call, "Illegal use of an unnamed polymorphic record, %s", s);
- gb_string_free(s);
- operand->mode = Addressing_Invalid;
- operand->type = t_invalid;;
- return Expr_Expr;
- }
- auto err = check_polymorphic_record_type(c, operand, call);
- if (err == 0) {
- Ast *ident = operand->expr;
- while (ident->kind == Ast_SelectorExpr) {
- Ast *s = ident->SelectorExpr.selector;
- ident = s;
- }
- 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, call, Addressing_Type, ot, empty_exact_value);
- } else {
- operand->mode = Addressing_Invalid;
- operand->type = t_invalid;
- }
- } else {
- CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion");
-
- operand->mode = Addressing_Invalid;
- isize arg_count = args.count;
- switch (arg_count) {
- case 0:
- {
- gbString str = type_to_string(t);
- error(call, "Missing argument in conversion to '%s'", str);
- gb_string_free(str);
- } break;
- default:
- {
- gbString str = type_to_string(t);
- error(call, "Too many arguments in conversion to '%s'", str);
- gb_string_free(str);
- } break;
- case 1: {
- Ast *arg = args[0];
- if (arg->kind == Ast_FieldValue) {
- error(call, "'field = value' cannot be used in a type conversion");
- arg = arg->FieldValue.value;
- // NOTE(bill): Carry on the cast regardless
- }
- check_expr_with_type_hint(c, operand, arg, t);
- if (operand->mode != Addressing_Invalid) {
- if (is_type_polymorphic(t)) {
- error(call, "A polymorphic type cannot be used in a type conversion");
- } else {
- // NOTE(bill): Otherwise the compiler can override the polymorphic type
- // as it assumes it is determining the type
- check_cast(c, operand, t);
- }
- }
- operand->type = t;
- operand->expr = call;
- if (operand->mode != Addressing_Invalid) {
- update_untyped_expr_type(c, arg, t, false);
- }
- break;
- }
- }
- }
- return Expr_Expr;
+ return check_call_expr_as_type_cast(c, operand, call, args, type_hint);
}
if (operand->mode == Addressing_Builtin) {
@@ -8028,7 +8356,13 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) {
if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
- error(call, "'context' has not been defined within this scope, but is required for this procedure call");
+ ERROR_BLOCK();
+ if (c->scope->flags & ScopeFlag_File) {
+ error(call, "Procedures requiring a 'context' cannot be called at the global scope");
+ } else {
+ error(call, "'context' has not been defined within this scope, but is required for this procedure call");
+ error_line("\tSuggestion: 'context = runtime.default_context()'");
+ }
}
}
@@ -8107,9 +8441,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
if (c->curr_proc_decl == nullptr) {
error(call, "Calling a '#force_inline' procedure that enables target features is not allowed at file scope");
} else {
- GB_ASSERT(c->curr_proc_decl->entity);
- GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
- String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature;
+ Entity *e = c->curr_proc_decl->entity.load();
+ GB_ASSERT(e);
+ GB_ASSERT(e->type->kind == Type_Proc);
+ String scope_features = e->type->Proc.enable_target_feature;
if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
ERROR_BLOCK();
error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
@@ -8132,7 +8467,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
type = pt;
}
type = base_type(type);
- if (type->kind == Type_Proc && type->Proc.optional_ok) {
+ if (type->kind == Type_Proc && type->Proc.optional_ok && type->Proc.result_count > 0) {
operand->mode = Addressing_OptionalOk;
operand->type = type->Proc.results->Tuple.variables[0]->type;
if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
@@ -8141,6 +8476,12 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
+ Entity *proc_entity = entity_from_expr(call->CallExpr.proc);
+ bool is_objc_call = proc_entity && proc_entity->kind == Entity_Procedure && proc_entity->Procedure.is_objc_impl_or_import;
+ if (is_objc_call) {
+ check_objc_call_expr(c, operand, call, proc_entity, pt);
+ }
+
return Expr_Expr;
}
@@ -8175,6 +8516,7 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
case Type_Basic:
if (t->Basic.kind == Basic_string) {
if (o->mode == Addressing_Constant) {
+ GB_ASSERT(o->value.kind == ExactValue_String);
*max_count = o->value.value_string.len;
}
if (o->mode != Addressing_Constant) {
@@ -8182,6 +8524,16 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
}
o->type = t_u8;
return true;
+ } else if (t->Basic.kind == Basic_string16) {
+ if (o->mode == Addressing_Constant) {
+ GB_ASSERT(o->value.kind == ExactValue_String16);
+ *max_count = o->value.value_string16.len;
+ }
+ if (o->mode != Addressing_Constant) {
+ o->mode = Addressing_Value;
+ }
+ o->type = t_u16;
+ return true;
} else if (t->Basic.kind == Basic_UntypedString) {
if (o->mode == Addressing_Constant) {
*max_count = o->value.value_string.len;
@@ -8391,7 +8743,7 @@ gb_internal bool check_range(CheckerContext *c, Ast *node, bool is_for_loop, Ope
return true;
}
-gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o) {
+gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o, Type *field_type) {
if (is_operand_nil(*o)) {
return true;
}
@@ -8406,6 +8758,13 @@ gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Opera
return true;
}
}
+ if (field_type != nullptr && is_type_typeid(field_type) && o->mode == Addressing_Type) {
+ add_type_info_type(c, o->type);
+ return true;
+ }
+ if (is_type_any(field_type)) {
+ return false;
+ }
return o->mode == Addressing_Constant;
}
@@ -8635,7 +8994,7 @@ gb_internal void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Op
isize count = multi_map_count(seen, key);
if (count) {
TEMPORARY_ALLOCATOR_GUARD();
- TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
+ TypeAndToken *taps = temporary_alloc_array<TypeAndToken>(count);
multi_map_get_all(seen, key, taps);
for (isize i = 0; i < count; i++) {
@@ -8709,23 +9068,52 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
String name = bd->name.string;
if (name == "file") {
String file = get_file_path_string(bd->token.pos.file_id);
- if (build_context.obfuscate_source_code_locations) {
+ switch (build_context.source_code_location_info) {
+ case SourceCodeLocationInfo_Normal:
+ break;
+ case SourceCodeLocationInfo_Obfuscated:
file = obfuscate_string(file, "F");
+ break;
+ case SourceCodeLocationInfo_Filename:
+ file = last_path_element(file);
+ break;
+ case SourceCodeLocationInfo_None:
+ file = str_lit("");
+ break;
}
o->type = t_untyped_string;
o->value = exact_value_string(file);
} else if (name == "directory") {
String file = get_file_path_string(bd->token.pos.file_id);
String path = dir_from_path(file);
- if (build_context.obfuscate_source_code_locations) {
+ switch (build_context.source_code_location_info) {
+ case SourceCodeLocationInfo_Normal:
+ break;
+ case SourceCodeLocationInfo_Obfuscated:
path = obfuscate_string(path, "D");
+ break;
+ case SourceCodeLocationInfo_Filename:
+ path = last_path_element(path);
+ break;
+ case SourceCodeLocationInfo_None:
+ path = str_lit("");
+ break;
}
o->type = t_untyped_string;
o->value = exact_value_string(path);
} else if (name == "line") {
i32 line = bd->token.pos.line;
- if (build_context.obfuscate_source_code_locations) {
+ switch (build_context.source_code_location_info) {
+ case SourceCodeLocationInfo_Normal:
+ break;
+ case SourceCodeLocationInfo_Obfuscated:
line = obfuscate_i32(line);
+ break;
+ case SourceCodeLocationInfo_Filename:
+ break;
+ case SourceCodeLocationInfo_None:
+ line = 0;
+ break;
}
o->type = t_untyped_integer;
o->value = exact_value_i64(line);
@@ -8736,8 +9124,17 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
o->value = exact_value_string(str_lit(""));
} else {
String p = c->proc_name;
- if (build_context.obfuscate_source_code_locations) {
+ switch (build_context.source_code_location_info) {
+ case SourceCodeLocationInfo_Normal:
+ break;
+ case SourceCodeLocationInfo_Obfuscated:
p = obfuscate_string(p, "P");
+ break;
+ case SourceCodeLocationInfo_Filename:
+ break;
+ case SourceCodeLocationInfo_None:
+ p = str_lit("");
+ break;
}
o->type = t_untyped_string;
o->value = exact_value_string(p);
@@ -8969,8 +9366,14 @@ gb_internal ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node
o->expr = node;
return Expr_Expr;
}
+
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_else_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(c, arg, x.mode, x.type, x.value);
+
bool y_is_diverging = false;
- check_expr_base(c, &y, default_value, x.type);
+ check_expr_base(c, &y, default_value, left_type);
switch (y.mode) {
case Addressing_NoValue:
if (is_diverging_expr(y.expr)) {
@@ -8995,14 +9398,21 @@ gb_internal ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node
return Expr_Expr;
}
- Type *left_type = nullptr;
- Type *right_type = nullptr;
- check_or_else_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(c, arg, x.mode, x.type, x.value);
-
if (left_type != nullptr) {
if (!y_is_diverging) {
- check_assignment(c, &y, left_type, name);
+ if (is_type_tuple(left_type)) {
+ if (!is_type_tuple(y.type)) {
+ error(y.expr, "Found a single value where a %td-valued expression was expected", left_type->Tuple.variables.count);
+ } else if (!are_types_identical(left_type, y.type)) {
+ gbString xt = type_to_string(left_type);
+ gbString yt = type_to_string(y.type);
+ error(y.expr, "Mismatched types, expected (%s), got (%s)", xt, yt);
+ gb_string_free(yt);
+ gb_string_free(xt);
+ }
+ } else {
+ check_assignment(c, &y, left_type, name);
+ }
}
} else {
check_or_else_expr_no_value_error(c, name, x, type_hint);
@@ -9305,8 +9715,7 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
break;
}
}
- if (is_constant &&
- (is_type_any(ft) || is_type_union(ft) || is_type_raw_union(ft) || is_type_typeid(ft))) {
+ if (is_constant && elem_cannot_be_constant(ft)) {
is_constant = false;
}
}
@@ -9341,11 +9750,11 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
Operand o = {};
check_expr_or_type(c, &o, fv->value, field->type);
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
+ if (elem_cannot_be_constant(field->type)) {
is_constant = false;
}
if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
+ is_constant = check_is_operand_compound_lit_constant(c, &o, field->type);
}
u8 prev_bit_field_bit_size = c->bit_field_bit_size;
@@ -9378,7 +9787,10 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
}
gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCompoundLit *cl) {
- if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0) {
+ if (cl->elems.count == 0) {
+ return false;
+ }
+ if ((check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0 && !build_context.dynamic_literals) {
ERROR_BLOCK();
error(node, "Compound literals of dynamic types are disabled by default");
error_line("\tSuggestion: If you want to enable them for this specific file, add '#+feature dynamic-literals' at the top of the file\n");
@@ -9389,9 +9801,31 @@ gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCom
error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n");
}
return false;
+ } else if (c->curr_proc_decl != nullptr && c->curr_proc_calling_convention != ProcCC_Odin) {
+ if (c->scope != nullptr && (c->scope->flags & ScopeFlag_ContextDefined) == 0) {
+ error(node, "Compound literals of dynamic types require a 'context' to defined");
+ }
}
- return cl->elems.count > 0;
+ return true;
+}
+
+gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node) {
+ // TODO(bill): per file `#+feature` flags
+ u64 flags = check_feature_flags(c, node);
+ if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Trap) != 0) {
+ return IntegerDivisionByZero_Trap;
+ }
+ if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) != 0) {
+ return IntegerDivisionByZero_Zero;
+ }
+ if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Self) != 0) {
+ return IntegerDivisionByZero_Self;
+ }
+ if ((flags & OptInFeatureFlag_IntegerDivisionByZero_AllBits) != 0) {
+ return IntegerDivisionByZero_AllBits;
+ }
+ return build_context.integer_division_by_zero_behaviour;
}
gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
@@ -9404,6 +9838,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
bool is_to_be_determined_array_count = false;
bool is_constant = true;
+ bool is_soa = false;
Ast *type_expr = cl->type;
@@ -9436,8 +9871,14 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name.string;
if (name == "soa") {
- error(node, "#soa arrays are not supported for compound literals");
- return kind;
+ is_soa = true;
+ if (count == nullptr) {
+ error(node, "#soa slices are not supported for compound literals");
+ return kind;
+ } else if (count->kind == Ast_UnaryExpr &&
+ count->UnaryExpr.op.kind == Token_Question) {
+ error(node, "#soa fixed length arrays must specify their length and cannot use ?");
+ }
}
}
}
@@ -9447,7 +9888,8 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name.string;
if (name == "soa") {
- error(node, "#soa arrays are not supported for compound literals");
+ is_soa = true;
+ error(node, "#soa dynamic arrays are not supported for compound literals");
return kind;
}
}
@@ -9476,101 +9918,101 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
switch (t->kind) {
- case Type_Struct: {
+ case Type_Struct:
if (cl->elems.count == 0) {
break; // NOTE(bill): No need to init
}
- if (t->Struct.soa_kind != StructSoa_None) {
- error(node, "#soa arrays are not supported for compound literals");
- break;
- }
+ if (t->Struct.soa_kind == StructSoa_None) {
+ if (t->Struct.is_raw_union) {
+ if (cl->elems.count > 0) {
+ // NOTE: unions cannot be constant
+ is_constant = elem_type_can_be_constant(t);
- if (t->Struct.is_raw_union) {
- if (cl->elems.count > 0) {
- // NOTE: unions cannot be constant
- is_constant = false;
-
- if (cl->elems[0]->kind != Ast_FieldValue) {
- gbString type_str = type_to_string(type);
- error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str);
- gb_string_free(type_str);
- } else {
- if (cl->elems.count != 1) {
+ if (cl->elems[0]->kind != Ast_FieldValue) {
gbString type_str = type_to_string(type);
- error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
+ error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str);
gb_string_free(type_str);
} else {
- check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
+ if (cl->elems.count != 1) {
+ gbString type_str = type_to_string(type);
+ error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
+ gb_string_free(type_str);
+ } else {
+ check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
+ }
}
}
- }
- break;
- }
-
- wait_signal_until_available(&t->Struct.fields_wait_signal);
- isize field_count = t->Struct.fields.count;
- isize min_field_count = t->Struct.fields.count;
- for (isize i = min_field_count-1; i >= 0; i--) {
- Entity *e = t->Struct.fields[i];
- GB_ASSERT(e->kind == Entity_Variable);
- if (e->Variable.param_value.kind != ParameterValue_Invalid) {
- min_field_count--;
- } else {
break;
}
- }
-
- if (cl->elems[0]->kind == Ast_FieldValue) {
- check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
- } else {
- bool seen_field_value = false;
- for_array(index, cl->elems) {
- Entity *field = nullptr;
- Ast *elem = cl->elems[index];
- if (elem->kind == Ast_FieldValue) {
- seen_field_value = true;
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- } else if (seen_field_value) {
- error(elem, "Value elements cannot be used after a 'field = value'");
- continue;
- }
- if (index >= field_count) {
- error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ wait_signal_until_available(&t->Struct.fields_wait_signal);
+ isize field_count = t->Struct.fields.count;
+ isize min_field_count = t->Struct.fields.count;
+ for (isize i = min_field_count-1; i >= 0; i--) {
+ Entity *e = t->Struct.fields[i];
+ GB_ASSERT(e->kind == Entity_Variable);
+ if (e->Variable.param_value.kind != ParameterValue_Invalid) {
+ min_field_count--;
+ } else {
break;
}
+ }
- if (field == nullptr) {
- field = t->Struct.fields[index];
- }
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
+ } else {
+ bool seen_field_value = false;
- Operand o = {};
- check_expr_or_type(c, &o, elem, field->type);
+ for_array(index, cl->elems) {
+ Entity *field = nullptr;
+ Ast *elem = cl->elems[index];
+ if (elem->kind == Ast_FieldValue) {
+ seen_field_value = true;
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ } else if (seen_field_value) {
+ error(elem, "Value elements cannot be used after a 'field = value'");
+ continue;
+ }
+ if (index >= field_count) {
+ error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ break;
+ }
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
- is_constant = false;
- }
- if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
- }
+ if (field == nullptr) {
+ field = t->Struct.fields[index];
+ }
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
- if (cl->elems.count < field_count) {
- if (min_field_count < field_count) {
- if (cl->elems.count < min_field_count) {
- error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count);
- }
- } else {
- error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ Operand o = {};
+ check_expr_or_type(c, &o, elem, field->type);
+
+ if (elem_cannot_be_constant(field->type)) {
+ is_constant = false;
+ }
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &o, field->type);
+ }
+
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+ if (cl->elems.count < field_count) {
+ if (min_field_count < field_count) {
+ if (cl->elems.count < min_field_count) {
+ error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count);
+ }
+ } else {
+ error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ }
}
}
- }
- break;
- }
+ break;
+ } else if (t->Struct.soa_kind != StructSoa_Fixed) {
+ error(node, "#soa slices and dynamic arrays are not supported for compound literals");
+ break;
+ }
+ /*fallthrough*/
case Type_Slice:
case Type_Array:
@@ -9581,7 +10023,14 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
Type *elem_type = nullptr;
String context_name = {};
i64 max_type_count = -1;
- if (t->kind == Type_Slice) {
+ if (t->kind == Type_Struct) {
+ GB_ASSERT(t->Struct.soa_kind == StructSoa_Fixed);
+ elem_type = t->Struct.soa_elem;
+ context_name = str_lit("#soa array literal");
+ if (!is_to_be_determined_array_count) {
+ max_type_count = t->Struct.soa_count;
+ }
+ } else if (t->kind == Type_Slice) {
elem_type = t->Slice.elem;
context_name = str_lit("slice literal");
} else if (t->kind == Type_Array) {
@@ -9590,6 +10039,12 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
if (!is_to_be_determined_array_count) {
max_type_count = t->Array.count;
}
+ } else if (t->kind == Type_Array) {
+ elem_type = t->Array.elem;
+ context_name = str_lit("array literal");
+ if (!is_to_be_determined_array_count) {
+ max_type_count = t->Array.count;
+ }
} else if (t->kind == Type_DynamicArray) {
elem_type = t->DynamicArray.elem;
context_name = str_lit("dynamic array literal");
@@ -9681,7 +10136,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
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 (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
+ }
} else {
Operand op_index = {};
check_expr(c, &op_index, fv->field);
@@ -9713,7 +10170,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
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 (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
+ }
}
}
@@ -9740,7 +10199,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
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 (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
+ }
}
if (max < index) {
@@ -9757,6 +10218,15 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max);
}
}
+ } else if (t->kind == Type_Struct) {
+ GB_ASSERT(t->Struct.soa_kind == StructSoa_Fixed);
+ if (is_to_be_determined_array_count) {
+ t->Struct.soa_count = cast(i32)max;
+ } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
+ if (0 < max && max < t->Struct.soa_count) {
+ error(node, "Expected %lld values for this #soa array literal, got %lld", cast(long long)t->Struct.soa_count, cast(long long)max);
+ }
+ }
}
@@ -9905,7 +10375,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
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 (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
+ }
TokenKind upper_op = Token_LtEq;
if (op.kind == Token_RangeHalf) {
@@ -9946,7 +10418,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
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 (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
+ }
add_to_seen_map(c, &seen, op_index);
}
@@ -9976,7 +10450,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
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 (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
+ }
}
if (max < index) {
@@ -10180,51 +10656,47 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
is_constant = false;
}
- if (cl->elems[0]->kind == Ast_FieldValue) {
- error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
- is_constant = false;
- } else {
- for (Ast *elem : cl->elems) {
- if (elem->kind == Ast_FieldValue) {
- error(elem, "'field = value' in a bit_set a literal is not allowed");
- continue;
- }
+ for (Ast *elem : cl->elems) {
+ if (elem->kind == Ast_FieldValue) {
+ error(elem, "'field = value' in a bit_set literal is not allowed");
+ is_constant = false;
+ continue;
+ }
- check_expr_with_type_hint(c, o, elem, et);
+ check_expr_with_type_hint(c, o, elem, et);
- if (is_constant) {
- is_constant = o->mode == Addressing_Constant;
- }
-
- if (elem->kind == Ast_BinaryExpr) {
- switch (elem->BinaryExpr.op.kind) {
- case Token_Or:
- {
- gbString x = expr_to_string(elem->BinaryExpr.left);
- gbString y = expr_to_string(elem->BinaryExpr.right);
- gbString e = expr_to_string(elem);
- error(elem, "Was the following intended? '%s, %s'; if not, surround the expression with parentheses '(%s)'", x, y, e);
- gb_string_free(e);
- gb_string_free(y);
- gb_string_free(x);
- }
- break;
+ if (is_constant) {
+ is_constant = o->mode == Addressing_Constant;
+ }
+
+ if (elem->kind == Ast_BinaryExpr) {
+ switch (elem->BinaryExpr.op.kind) {
+ case Token_Or:
+ {
+ gbString x = expr_to_string(elem->BinaryExpr.left);
+ gbString y = expr_to_string(elem->BinaryExpr.right);
+ gbString e = expr_to_string(elem);
+ error(elem, "Was the following intended? '%s, %s'; if not, surround the expression with parentheses '(%s)'", x, y, e);
+ gb_string_free(e);
+ gb_string_free(y);
+ gb_string_free(x);
}
+ break;
}
+ }
- check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
- if (o->mode == Addressing_Constant) {
- i64 lower = t->BitSet.lower;
- i64 upper = t->BitSet.upper;
- i64 v = exact_value_to_i64(o->value);
- if (lower <= v && v <= upper) {
- // okay
- } else {
- gbString s = expr_to_string(o->expr);
- error(elem, "Bit field value out of bounds, %s (%lld) not in the range %lld .. %lld", s, v, lower, upper);
- gb_string_free(s);
- continue;
- }
+ check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
+ if (o->mode == Addressing_Constant) {
+ i64 lower = t->BitSet.lower;
+ i64 upper = t->BitSet.upper;
+ i64 v = exact_value_to_i64(o->value);
+ if (lower <= v && v <= upper) {
+ // okay
+ } else {
+ gbString s = expr_to_string(o->expr);
+ error(elem, "Bit field value out of bounds, %s (%lld) not in the range %lld .. %lld", s, v, lower, upper);
+ gb_string_free(s);
+ continue;
}
}
}
@@ -10586,7 +11058,7 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
}
}
- auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
+ auto modified_args = permanent_slice_make<Ast *>(ce->args.count+1);
modified_args[0] = first_arg;
slice_copy(&modified_args, ce->args, 1);
ce->args = modified_args;
@@ -10751,9 +11223,17 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
valid = true;
if (o->mode == Addressing_Constant) {
+ GB_ASSERT(o->value.kind == ExactValue_String);
max_count = o->value.value_string.len;
}
o->type = type_deref(o->type);
+ } else if (t->Basic.kind == Basic_string16) {
+ valid = true;
+ if (o->mode == Addressing_Constant) {
+ GB_ASSERT(o->value.kind == ExactValue_String16);
+ max_count = o->value.value_string16.len;
+ }
+ o->type = type_deref(o->type);
}
break;
@@ -10908,15 +11388,21 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
o->expr = node;
return kind;
}
-
- String s = {};
- if (o->value.kind == ExactValue_String) {
- s = o->value.value_string;
- }
-
o->mode = Addressing_Constant;
o->type = t;
- o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
+
+ if (o->value.kind == ExactValue_String16) {
+ String16 s = o->value.value_string16;
+
+ o->value = exact_value_string16(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
+ } else {
+ String s = {};
+ if (o->value.kind == ExactValue_String) {
+ s = o->value.value_string;
+ }
+
+ o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
+ }
}
return kind;
}
@@ -10961,7 +11447,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
return kind;
case_end;
- case_ast_node(i, Implicit, node)
+ case_ast_node(i, Implicit, node);
switch (i->kind) {
case Token_context:
{
@@ -10997,7 +11483,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
case_ast_node(u, Uninit, node);
o->mode = Addressing_Value;
o->type = t_untyped_uninit;
- error(node, "Use of --- outside of variable declaration");
+ error(node, "Global variables will always be zeroed if left unassigned, --- is disallowed");
case_end;
@@ -11005,6 +11491,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
Type *t = t_invalid;
switch (node->tav.value.kind) {
case ExactValue_String: t = t_untyped_string; break;
+ case ExactValue_String16: t = t_string16; break; // TODO(bill): determine this correctly
case ExactValue_Float: t = t_untyped_float; break;
case ExactValue_Complex: t = t_untyped_complex; break;
case ExactValue_Quaternion: t = t_untyped_quaternion; break;
@@ -11074,6 +11561,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
o->mode = Addressing_Value;
o->type = type;
+ o->value = exact_value_procedure(node);
case_end;
case_ast_node(te, TernaryIfExpr, node);
@@ -11232,6 +11720,13 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
+ } else if (o->mode == Addressing_Type) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Cannot dereference '%s' because it is a type", str);
+
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
} else {
Type *t = base_type(o->type);
if (t->kind == Type_Pointer && !is_type_empty_union(t->Pointer.elem)) {
@@ -11434,6 +11929,8 @@ gb_internal bool is_exact_value_zero(ExactValue const &v) {
return !v.value_bool;
case ExactValue_String:
return v.value_string.len == 0;
+ case ExactValue_String16:
+ return v.value_string16.len == 0;
case ExactValue_Integer:
return big_int_is_zero(&v.value_integer);
case ExactValue_Float: