From 3b4a0e8b907bce9d5682b64ef02dfbf430762f5b Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 6 Apr 2025 13:22:22 -0400 Subject: Improve support for inferring the specific type of the `self` value in a method call. --- src/ast.c | 2 ++ src/ast.h | 5 +++++ src/compile.c | 33 +++++++++++++++++++++++++++++++++ src/typecheck.c | 1 + 4 files changed, 41 insertions(+) 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 #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, "%r", xml_escape(data.code)) T(Deserialize, "%r%r", type_ast_to_xml(data.type), ast_to_xml(data.value)) T(Extend, "%r", data.name, ast_to_xml(data.body)) + T(ExplicitlyTyped, "%r", 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 -- cgit v1.2.3