aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-04-06 13:22:22 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-04-06 13:22:22 -0400
commit3b4a0e8b907bce9d5682b64ef02dfbf430762f5b (patch)
tree93eca6cc83c624abbe2ed56c65df1dd524ad6a3a
parent0a54b8965319545741c427086d8994f77a72089f (diff)
Improve support for inferring the specific type of the `self` value in a
method call.
-rw-r--r--src/ast.c2
-rw-r--r--src/ast.h5
-rw-r--r--src/compile.c33
-rw-r--r--src/typecheck.c1
4 files changed, 41 insertions, 0 deletions
diff --git a/src/ast.c b/src/ast.c
index 8544ebad..d4ecca2d 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -4,6 +4,7 @@
#include <stdarg.h>
#include "ast.h"
+#include "types.h"
#include "stdlib/datatypes.h"
#include "stdlib/integers.h"
#include "stdlib/tables.h"
@@ -193,6 +194,7 @@ CORD ast_to_xml(ast_t *ast)
T(InlineCCode, "<InlineCode>%r</InlineCode>", xml_escape(data.code))
T(Deserialize, "<Deserialize><type>%r</type>%r</Deserialize>", type_ast_to_xml(data.type), ast_to_xml(data.value))
T(Extend, "<Extend name=\"%s\">%r</Extend>", data.name, ast_to_xml(data.body))
+ T(ExplicitlyTyped, "<ExplicitlyTyped type=\"%r\">%r</ExplicitlyTyped>", type_to_cord(data.type), ast_to_xml(data.ast))
default: return "???";
#undef T
}
diff --git a/src/ast.h b/src/ast.h
index 4f370e11..3beb6be2 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -150,6 +150,7 @@ typedef enum {
InlineCCode,
Deserialize,
Extend,
+ ExplicitlyTyped,
} ast_e;
struct ast_s {
@@ -338,6 +339,10 @@ struct ast_s {
const char *name;
ast_t *body;
} Extend;
+ struct {
+ ast_t *ast;
+ struct type_s *type;
+ } ExplicitlyTyped;
} __data;
};
diff --git a/src/compile.c b/src/compile.c
index 20afec7a..ad3097a7 100644
--- a/src/compile.c
+++ b/src/compile.c
@@ -450,6 +450,10 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t
add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Deserialize)->value);
break;
}
+ case ExplicitlyTyped: {
+ add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, ExplicitlyTyped)->ast);
+ break;
+ }
case Use: case FunctionDef: case ConvertDef: case StructDef: case EnumDef: case LangDef: case Extend: {
errx(1, "Definitions should not be reachable in a closure.");
}
@@ -2014,6 +2018,32 @@ CORD compile_to_type(env_t *env, ast_t *ast, type_t *t)
type_t *actual = get_type(env, ast);
+ // Edge case: there are some situations where a method call needs to have
+ // the `self` value get compiled to a specific type that can't be fully
+ // inferred from the expression itself. We can infer what the specific type
+ // should be from what we know the specific type of the return value is,
+ // but it requires a bit of special logic.
+ // For example:
+ // x : [Int?] = [none]:sorted()
+ // Here, we know that `[none]` is `[Int?]`, but we need to thread that
+ // information through the compiler using an `ExplicitlyTyped` node.
+ if (ast->tag == MethodCall) {
+ auto methodcall = Match(ast, MethodCall);
+ type_t *self_type = get_type(env, methodcall->self);
+ // Currently, this is only implemented for cases where you have the return type
+ // and the self type equal to each other, because that's the main case I care
+ // about with array and set methods (e.g. `Array.sorted()`)
+ if (is_incomplete_type(self_type) && type_eq(self_type, actual)) {
+ type_t *completed_self = most_complete_type(self_type, t);
+ if (completed_self) {
+ ast_t *explicit_self = WrapAST(methodcall->self, ExplicitlyTyped, .ast=methodcall->self, .type=completed_self);
+ ast_t *new_methodcall = WrapAST(ast, MethodCall, .self=explicit_self,
+ .name=methodcall->name, .args=methodcall->args);
+ return compile_to_type(env, new_methodcall, t);
+ }
+ }
+ }
+
// Promote values to views-of-values if needed:
if (t->tag == PointerType && Match(t, PointerType)->is_stack && actual->tag != PointerType)
return CORD_all("stack(", compile_to_type(env, ast, Match(t, PointerType)->pointed), ")");
@@ -3316,6 +3346,9 @@ CORD compile(env_t *env, ast_t *ast)
"generic_deserialize(", compile(env, value), ", &deserialized, ", compile_type_info(t), ");\n"
"deserialized; })");
}
+ case ExplicitlyTyped: {
+ return compile_to_type(env, Match(ast, ExplicitlyTyped)->ast, get_type(env, ast));
+ }
case When: {
auto original = Match(ast, When);
ast_t *when_var = WrapAST(ast, Var, .name="when");
diff --git a/src/typecheck.c b/src/typecheck.c
index aee96234..c6f8b10f 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -1482,6 +1482,7 @@ type_t *get_type(env_t *env, ast_t *ast)
}
case Unknown: code_err(ast, "I can't figure out the type of: ", ast_to_xml_str(ast));
case Deserialize: return parse_type_ast(env, Match(ast, Deserialize)->type);
+ case ExplicitlyTyped: return Match(ast, ExplicitlyTyped)->type;
}
#ifdef __GNUC__
#pragma GCC diagnostic pop