aboutsummaryrefslogtreecommitdiff
path: root/src/typecheck.c
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-08-24 14:18:22 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-08-24 14:18:22 -0400
commitbe534f5e511a1d3836254b827cdaa2b8c309f5c4 (patch)
tree478fc9ede3a1c61220d3b02a2524fb32d53e7300 /src/typecheck.c
parent788922fa2437c5b680b57d8adaaac9012fd5aba4 (diff)
Constructors and functions with underscore arguments should be allowed to be called, but
only if the underscore arguments are not provided but are implicit from the default values. Same for constructing structs with private fields.
Diffstat (limited to 'src/typecheck.c')
-rw-r--r--src/typecheck.c53
1 files changed, 31 insertions, 22 deletions
diff --git a/src/typecheck.c b/src/typecheck.c
index 928f4b14..db55ebac 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -1300,7 +1300,7 @@ type_t *get_type(env_t *env, ast_t *ast) {
DeclareMatch(fn, b->type, FunctionType);
if (type_eq(fn->ret, rhs_t)) {
arg_ast_t *args = new (arg_ast_t, .value = binop.rhs, .next = new (arg_ast_t, .value = binop.lhs));
- if (is_valid_call(env, fn->args, args, true)) return rhs_t;
+ if (is_valid_call(env, fn->args, args, (call_opts_t){.promotion = true})) return rhs_t;
}
}
} else if (ast->tag == Multiply && is_numeric_type(rhs_t)) {
@@ -1309,7 +1309,7 @@ type_t *get_type(env_t *env, ast_t *ast) {
DeclareMatch(fn, b->type, FunctionType);
if (type_eq(fn->ret, lhs_t)) {
arg_ast_t *args = new (arg_ast_t, .value = binop.lhs, .next = new (arg_ast_t, .value = binop.rhs));
- if (is_valid_call(env, fn->args, args, true)) return lhs_t;
+ if (is_valid_call(env, fn->args, args, (call_opts_t){.promotion = true})) return lhs_t;
}
}
} else if ((ast->tag == Divide || ast->tag == Mod || ast->tag == Mod1) && is_numeric_type(rhs_t)) {
@@ -1318,7 +1318,7 @@ type_t *get_type(env_t *env, ast_t *ast) {
DeclareMatch(fn, b->type, FunctionType);
if (type_eq(fn->ret, lhs_t)) {
arg_ast_t *args = new (arg_ast_t, .value = binop.lhs, .next = new (arg_ast_t, .value = binop.rhs));
- if (is_valid_call(env, fn->args, args, true)) return lhs_t;
+ if (is_valid_call(env, fn->args, args, (call_opts_t){.promotion = true})) return lhs_t;
}
}
}
@@ -1606,19 +1606,22 @@ type_t *get_arg_type(env_t *env, arg_t *arg) {
return get_type(env, arg->default_val);
}
-bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed) {
+bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, call_opts_t options) {
Table_t used_args = {};
// Populate keyword args:
for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
if (!call_arg->name) continue;
+ if (!options.underscores && call_arg->name[0] == '_') return false;
- type_t *call_type = get_arg_ast_type(env, call_arg);
for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) {
+ if (!options.underscores && spec_arg->name[0] == '_') continue;
if (!streq(call_arg->name, spec_arg->name)) continue;
type_t *spec_type = get_arg_type(env, spec_arg);
- if (promotion_allowed) {
- if (!can_compile_to_type(env, call_arg->value, spec_type))
+ env_t *arg_scope = with_enum_scope(env, spec_type);
+ type_t *call_type = get_arg_ast_type(arg_scope, call_arg);
+ if (options.promotion) {
+ if (!can_compile_to_type(arg_scope, call_arg->value, spec_type))
return false; // Positional arg trying to fill in
} else {
type_t *complete_call_type =
@@ -1638,22 +1641,25 @@ bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool prom
arg_ast_t *keyworded = Table$str_get(used_args, spec_arg->name);
if (keyworded) continue;
- type_t *spec_type = get_arg_type(env, spec_arg);
- for (; unused_args; unused_args = unused_args->next) {
- if (unused_args->name) continue; // Already handled the keyword args
- if (promotion_allowed) {
- if (!can_compile_to_type(env, unused_args->value, spec_type))
- return false; // Positional arg trying to fill in
- } else {
- type_t *call_type = get_arg_ast_type(env, unused_args);
- type_t *complete_call_type =
- is_incomplete_type(call_type) ? most_complete_type(call_type, spec_type) : call_type;
- if (!complete_call_type) return false;
- if (!type_eq(complete_call_type, spec_type)) return false; // Positional arg trying to fill in
+ if (spec_arg->name[0] != '_' || options.underscores) {
+ type_t *spec_type = get_arg_type(env, spec_arg);
+ env_t *arg_scope = with_enum_scope(env, spec_type);
+ for (; unused_args; unused_args = unused_args->next) {
+ if (unused_args->name) continue; // Already handled the keyword args
+ if (options.promotion) {
+ if (!can_compile_to_type(arg_scope, unused_args->value, spec_type))
+ return false; // Positional arg trying to fill in
+ } else {
+ type_t *call_type = get_arg_ast_type(arg_scope, unused_args);
+ type_t *complete_call_type =
+ is_incomplete_type(call_type) ? most_complete_type(call_type, spec_type) : call_type;
+ if (!complete_call_type) return false;
+ if (!type_eq(complete_call_type, spec_type)) return false; // Positional arg trying to fill in
+ }
+ Table$str_set(&used_args, spec_arg->name, unused_args);
+ unused_args = unused_args->next;
+ goto found_it;
}
- Table$str_set(&used_args, spec_arg->name, unused_args);
- unused_args = unused_args->next;
- goto found_it;
}
if (spec_arg->default_val) goto found_it;
@@ -1753,6 +1759,9 @@ PUREFUNC bool can_compile_to_type(env_t *env, ast_t *ast, type_t *needed) {
type_t *actual = get_type(env, ast);
if (actual->tag == OptionalType && needed->tag == OptionalType) return can_promote(actual, needed);
+ if (is_numeric_type(needed) && ast->tag == Int) return true;
+ if (needed->tag == NumType && ast->tag == Num) return true;
+
needed = non_optional(needed);
if (needed->tag == ListType && ast->tag == List) {
type_t *item_type = Match(needed, ListType)->item_type;