aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compile.c50
-rw-r--r--src/environment.c28
-rw-r--r--src/typecheck.c53
-rw-r--r--src/typecheck.h8
4 files changed, 80 insertions, 59 deletions
diff --git a/src/compile.c b/src/compile.c
index 6bb528a0..262608c5 100644
--- a/src/compile.c
+++ b/src/compile.c
@@ -579,7 +579,7 @@ static Text_t compile_binary_op(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))
+ if (is_valid_call(env, fn->args, args, (call_opts_t){.promotion = true}))
return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")");
}
}
@@ -589,7 +589,7 @@ static Text_t compile_binary_op(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))
+ if (is_valid_call(env, fn->args, args, (call_opts_t){.promotion = true}))
return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")");
}
}
@@ -599,7 +599,7 @@ static Text_t compile_binary_op(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))
+ if (is_valid_call(env, fn->args, args, (call_opts_t){.promotion = true}))
return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")");
}
}
@@ -609,7 +609,7 @@ static Text_t compile_binary_op(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))
+ if (is_valid_call(env, fn->args, args, (call_opts_t){.promotion = true}))
return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")");
}
}
@@ -3466,6 +3466,24 @@ Text_t compile(env_t *env, ast_t *ast) {
type_t *fn_t = get_type(env, call->fn);
if (fn_t->tag == FunctionType) {
Text_t fn = compile(env, call->fn);
+ if (!is_valid_call(env, Match(fn_t, FunctionType)->args, call->args, (call_opts_t){.promotion = true})) {
+ if (is_valid_call(env, Match(fn_t, FunctionType)->args, call->args,
+ (call_opts_t){.promotion = true, .underscores = true})) {
+ code_err(ast, "You can't pass underscore arguments to this function (those are private)");
+ } else {
+ arg_t *args = NULL;
+ for (arg_ast_t *a = call->args; a; a = a->next)
+ args = new (arg_t, .name = a->name, .type = get_type(env, a->value), .next = args);
+ REVERSE_LIST(args);
+ code_err(ast,
+ "This function's public signature doesn't match this call site.\n"
+ "The signature is: ",
+ type_to_text(fn_t),
+ "\n"
+ "But it's being called with: ",
+ type_to_text(Type(FunctionType, .args = args)));
+ }
+ }
return Texts(fn, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, call->args), ")");
} else if (fn_t->tag == TypeInfoType) {
type_t *t = Match(fn_t, TypeInfoType)->type;
@@ -3509,21 +3527,19 @@ Text_t compile(env_t *env, ast_t *ast) {
")");
} else if (t->tag == StructType) {
DeclareMatch(struct_, t, StructType);
- if (!struct_->opaque && is_valid_call(env, struct_->fields, call->args, true)) {
- if (env->current_type == NULL || !type_eq(env->current_type, t)) {
- for (arg_t *field = struct_->fields; field; field = field->next) {
- if (field->name[0] == '_')
- code_err(ast, "This struct can't be "
- "initialized directly because it "
- "has private fields (starting "
- "with underscore).\n"
- "Use a `convert` or `func` to "
- "instantiate the type "
- "instead.");
- }
- }
+ if (struct_->opaque) code_err(ast, "This struct is opaque, so I don't know what's inside it!");
+
+ call_opts_t constructor_opts = {
+ .promotion = true,
+ .underscores = (env->current_type != NULL && type_eq(env->current_type, t)),
+ };
+ if (is_valid_call(env, struct_->fields, call->args, constructor_opts)) {
return Texts("((", compile_type(t), "){", compile_arguments(env, ast, struct_->fields, call->args),
"})");
+ } else if (!constructor_opts.underscores
+ && is_valid_call(env, struct_->fields, call->args,
+ (call_opts_t){.promotion = true, .underscores = true})) {
+ code_err(ast, "This constructor uses private fields that are not exposed.");
}
}
code_err(ast,
diff --git a/src/environment.c b/src/environment.c
index 848e1269..f7956054 100644
--- a/src/environment.c
+++ b/src/environment.c
@@ -718,28 +718,18 @@ PUREFUNC binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args, bool
if (!type_env) return NULL;
List_t constructors = type_env->namespace->constructors;
// Prioritize exact matches:
+ call_opts_t options = {.promotion = false, .underscores = allow_underscores};
for (int64_t i = constructors.length - 1; i >= 0; i--) {
- binding_t *b = constructors.data + i * constructors.stride;
- DeclareMatch(fn, b->type, FunctionType);
- if (!allow_underscores) {
- for (arg_t *arg = fn->args; arg; arg = arg->next)
- if (arg->name[0] == '_') goto next_constructor;
- }
- if (type_eq(fn->ret, t) && is_valid_call(env, fn->args, args, false)) return b;
- next_constructor:
- continue;
+ binding_t *constructor = constructors.data + i * constructors.stride;
+ DeclareMatch(fn, constructor->type, FunctionType);
+ if (type_eq(fn->ret, t) && is_valid_call(env, fn->args, args, options)) return constructor;
}
// Fall back to promotion:
+ options.promotion = true;
for (int64_t i = constructors.length - 1; i >= 0; i--) {
- binding_t *b = constructors.data + i * constructors.stride;
- DeclareMatch(fn, b->type, FunctionType);
- if (!allow_underscores) {
- for (arg_t *arg = fn->args; arg; arg = arg->next)
- if (arg->name[0] == '_') goto next_constructor2;
- }
- if (type_eq(fn->ret, t) && is_valid_call(env, fn->args, args, true)) return b;
- next_constructor2:
- continue;
+ binding_t *constructor = constructors.data + i * constructors.stride;
+ DeclareMatch(fn, constructor->type, FunctionType);
+ if (type_eq(fn->ret, t) && is_valid_call(env, fn->args, args, options)) return constructor;
}
return NULL;
}
@@ -752,7 +742,7 @@ PUREFUNC binding_t *get_metamethod_binding(env_t *env, ast_e tag, ast_t *lhs, as
DeclareMatch(fn, b->type, FunctionType);
if (!type_eq(fn->ret, ret)) return NULL;
arg_ast_t *args = new (arg_ast_t, .value = lhs, .next = new (arg_ast_t, .value = rhs));
- return is_valid_call(env, fn->args, args, true) ? b : NULL;
+ return is_valid_call(env, fn->args, args, (call_opts_t){.promotion = true}) ? b : NULL;
}
void set_binding(env_t *env, const char *name, type_t *type, Text_t code) {
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;
diff --git a/src/typecheck.h b/src/typecheck.h
index b2be77bf..34fc33a1 100644
--- a/src/typecheck.h
+++ b/src/typecheck.h
@@ -4,6 +4,7 @@
#include <gc.h>
#include <stdarg.h>
+#include <stdbool.h>
#include "ast.h"
#include "environment.h"
@@ -25,7 +26,12 @@ PUREFUNC bool can_be_mutated(env_t *env, ast_t *ast);
type_t *parse_type_string(env_t *env, const char *str);
type_t *get_method_type(env_t *env, ast_t *self, const char *name);
PUREFUNC bool is_constant(env_t *env, ast_t *ast);
-bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed);
PUREFUNC bool can_compile_to_type(env_t *env, ast_t *ast, type_t *needed);
+typedef struct {
+ bool promotion : 1, underscores : 1;
+} call_opts_t;
+
+bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, call_opts_t options);
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0