aboutsummaryrefslogtreecommitdiff
path: root/src/check_expr.cpp
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2026-01-30 10:49:55 +0000
committerGitHub <noreply@github.com>2026-01-30 10:49:55 +0000
commit19b545e7cb0b09a7c8b3424ca8276b9e37f8de80 (patch)
tree974b844bf082c526f6a12396c4a80adfc73b9a60 /src/check_expr.cpp
parent8b745c3909a3482aebe27998d8b870286e448e35 (diff)
parent5a21213fa5e2c74d5021adb2a87f0cc441a38eab (diff)
Merge branch 'master' into bill/feature-using-stmt
Diffstat (limited to 'src/check_expr.cpp')
-rw-r--r--src/check_expr.cpp152
1 files changed, 104 insertions, 48 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 2fe6c0251..1c09ad908 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -1133,7 +1133,11 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
x.mode = Addressing_Value;
x.type = t;
if (check_is_assignable_to(c, &x, type)) {
- add_entity_use(c, operand->expr, e);
+ if (operand->expr->kind == Ast_SelectorExpr) {
+ add_entity_use(c, operand->expr->SelectorExpr.selector, e);
+ } else {
+ add_entity_use(c, operand->expr, e);
+ }
good = true;
break;
}
@@ -3526,28 +3530,17 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type)
Type *elem = core_array_type(bt);
if (core_type(bt)->kind == Type_Basic) {
- if (check_representable_as_constant(c, x->value, type, &x->value)) {
- return true;
- }
- goto check_castable;
- } else if (!are_types_identical(elem, bt) &&
- elem->kind == Type_Basic &&
- check_representable_as_constant(c, x->value, elem, &x->value)) {
- if (check_representable_as_constant(c, x->value, type, &x->value)) {
- return true;
- }
- goto check_castable;
+ return check_representable_as_constant(c, x->value, type, &x->value) ||
+ (is_type_pointer(type) && check_is_castable_to(c, x, type));
+ } else if (!are_types_identical(elem, bt) && elem->kind == Type_Basic && x->type->kind == Type_Basic) {
+ return check_representable_as_constant(c, x->value, elem, &x->value) ||
+ (is_type_pointer(elem) && check_is_castable_to(c, x, elem));
} else if (check_is_castable_to(c, x, type)) {
x->value = {};
x->mode = Addressing_Value;
return true;
}
-
- return false;
- }
-
-check_castable:
- if (check_is_castable_to(c, x, type)) {
+ } else if (check_is_castable_to(c, x, type)) {
if (x->mode != Addressing_Constant) {
x->mode = Addressing_Value;
} else if (is_type_slice(type) && is_type_string(x->type)) {
@@ -4132,15 +4125,19 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
i64 upper = yt->BitSet.upper;
if (lower <= key && key <= upper) {
- i64 bit = 1ll<<key;
- i64 bits = big_int_to_i64(&v.value_integer);
+ BigInt idx = big_int_make_i64(key - lower);
+ BigInt bit = big_int_make_i64(1);
+ big_int_shl_eq(&bit, &idx);
+
+ BigInt mask = {};
+ big_int_and(&mask, &bit, &v.value_integer);
x->mode = Addressing_Constant;
x->type = t_untyped_bool;
if (op.kind == Token_in) {
- x->value = exact_value_bool((bit & bits) != 0);
+ x->value = exact_value_bool(!big_int_is_zero(&mask));
} else {
- x->value = exact_value_bool((bit & bits) == 0);
+ x->value = exact_value_bool(big_int_is_zero(&mask));
}
x->expr = node;
return;
@@ -4707,12 +4704,14 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
switch (t->kind) {
case Type_Basic:
if (operand->mode == Addressing_Constant) {
- check_is_expressible(c, operand, t);
+ check_is_expressible(c, operand, target_type);
if (operand->mode == Addressing_Invalid) {
return;
}
update_untyped_expr_value(c, operand->expr, operand->value);
- } else {
+ }
+
+ {
switch (operand->type->Basic.kind) {
case Basic_UntypedBool:
if (!is_type_boolean(target_type)) {
@@ -5195,6 +5194,11 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
}
} else {
TypeAndValue index_tav = fv->field->tav;
+ if (index_tav.mode != Addressing_Constant) {
+ if (success_) *success_ = false;
+ if (finish_) *finish_ = true;
+ return empty_exact_value;
+ }
GB_ASSERT(index_tav.mode == Addressing_Constant);
ExactValue index_value = index_tav.value;
if (is_type_enumerated_array(node->tav.type)) {
@@ -8172,13 +8176,7 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast *
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 (proc_entity->Procedure.is_objc_class_method) {
if (is_return_instancetype) {
if (ce->proc->kind == Ast_SelectorExpr) {
ast_node(se, SelectorExpr, ce->proc);
@@ -8194,7 +8192,8 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast *
self_type = t_objc_Class;
params_start = 0;
- } else if (ce->args.count > 0) {
+ } else {
+ GB_ASSERT(ce->args.count > 0);
GB_ASSERT(is_type_objc_ptr_to_object(params[0]->type));
if (ce->args[0]->tav.objc_super_target) {
@@ -8226,7 +8225,7 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast *
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) {
+gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, ProcTailing tailing, Type *type_hint) {
if (proc != nullptr &&
proc->kind == Ast_BasicDirective) {
ast_node(bd, BasicDirective, proc);
@@ -8257,7 +8256,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
return Expr_Expr;
}
if (inlining != ProcInlining_none) {
- error(call, "Inlining operators are not allowed on built-in procedures");
+ error(call, "Inlining directives are not allowed on built-in procedures");
+ }
+ if (tailing != ProcTailing_none) {
+ error(call, "Tailing directives are not allowed on built-in procedures");
}
} else {
if (proc != nullptr) {
@@ -8399,6 +8401,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
bool is_call_inlined = false;
+ bool is_call_tailed = true;
switch (inlining) {
case ProcInlining_inline:
@@ -8433,6 +8436,23 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
+ switch (tailing) {
+ case ProcTailing_none:
+ break;
+ case ProcTailing_must_tail:
+ is_call_tailed = true;
+ if (c->curr_proc_sig == nullptr || !are_types_identical(c->curr_proc_sig, pt)) {
+ ERROR_BLOCK();
+ gbString a = type_to_string(pt);
+ gbString b = type_to_string(c->curr_proc_sig);
+ error(call, "Use of '#must_tail' of a procedure must have the same type as the procedure it was called within");
+ error_line("\tCall type: %s, parent type: %s", a, b);
+ gb_string_free(b);
+ gb_string_free(a);
+ }
+ break;
+ }
+
{
String invalid;
if (pt->kind == Type_Proc && pt->Proc.require_target_feature.len != 0) {
@@ -8759,6 +8779,14 @@ gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Opera
if (is_operand_nil(*o)) {
return true;
}
+ if (is_type_any(field_type)) {
+ return false;
+ }
+ if (field_type != nullptr && is_type_typeid(field_type) && o->mode == Addressing_Type) {
+ add_type_info_type(c, o->type);
+ return true;
+ }
+
Ast *expr = unparen_expr(o->expr);
if (expr != nullptr) {
Entity *e = strip_entity_wrapping(entity_from_expr(expr));
@@ -8769,13 +8797,12 @@ gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Opera
add_type_and_value(c, expr, Addressing_Constant, type_of_expr(expr), exact_value_procedure(expr));
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;
+
+ if (e != nullptr && e->kind == Entity_Variable && e->Variable.is_rodata) {
+ // DeclInfo *d = e->decl_info;
+ // TODO(bill): rodata inlining for non-indirect values
+ // e.g. **NOT** []T{...}
+ }
}
return o->mode == Addressing_Constant;
}
@@ -11044,8 +11071,20 @@ gb_internal ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *no
end:;
if ((c->state_flags & StateFlag_no_type_assert) == 0) {
- add_package_dependency(c, "runtime", "type_assertion_check");
- add_package_dependency(c, "runtime", "type_assertion_check2");
+ bool has_context = true;
+ if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) {
+ has_context = false;
+ } else if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
+ has_context = false;
+ }
+
+ if (has_context) {
+ add_package_dependency(c, "runtime", "type_assertion_check_with_context");
+ add_package_dependency(c, "runtime", "type_assertion_check2_with_context");
+ } else {
+ add_package_dependency(c, "runtime", "type_assertion_check_contextless");
+ add_package_dependency(c, "runtime", "type_assertion_check2_contextless");
+ }
}
return kind;
}
@@ -11570,6 +11609,15 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
return kind;
case_end;
+ case_ast_node(ht, HelperType, node);
+ Type *type = check_type(c, ht->type);
+ if (type != nullptr && type != t_invalid) {
+ o->mode = Addressing_Type;
+ o->type = type;
+ }
+ return kind;
+ case_end;
+
case_ast_node(i, Implicit, node);
switch (i->kind) {
case Token_context:
@@ -11832,7 +11880,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
case_end;
case_ast_node(ce, CallExpr, node);
- return check_call_expr(c, o, node, ce->proc, ce->args, ce->inlining, type_hint);
+ return check_call_expr(c, o, node, ce->proc, ce->args, ce->inlining, ce->tailing, type_hint);
case_end;
case_ast_node(de, DerefExpr, node);
@@ -12103,12 +12151,12 @@ gb_internal bool is_exact_value_zero(ExactValue const &v) {
-gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y, bool *do_break_) {
+gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y) {
ast_node(x_cl, CompoundLit, x.value_compound);
ast_node(y_cl, CompoundLit, y.value_compound);
if (x_cl->elems.count != y_cl->elems.count) {
- if (do_break_) *do_break_ = true;
+ return false;
}
bool test = op == Token_CmpEq;
@@ -12573,6 +12621,12 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
case_end;
case_ast_node(ce, CallExpr, node);
+ switch (ce->tailing) {
+ case ProcTailing_must_tail:
+ str = gb_string_appendc(str, "#must_tail ");
+ break;
+ }
+
switch (ce->inlining) {
case ProcInlining_inline:
str = gb_string_appendc(str, "#force_inline ");
@@ -12640,8 +12694,10 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
str = write_expr_to_string(str, st->polymorphic_params, shorthand);
str = gb_string_appendc(str, ") ");
}
- if (st->is_packed) str = gb_string_appendc(str, "#packed ");
- if (st->is_raw_union) str = gb_string_appendc(str, "#raw_union ");
+ if (st->is_packed) str = gb_string_appendc(str, "#packed ");
+ if (st->is_raw_union) str = gb_string_appendc(str, "#raw_union ");
+ if (st->is_all_or_none) str = gb_string_appendc(str, "#all_or_none ");
+ if (st->is_simple) str = gb_string_appendc(str, "#simple ");
if (st->align) {
str = gb_string_appendc(str, "#align ");
str = write_expr_to_string(str, st->align, shorthand);