* Quick-Functions
* do-expressions
* typeof
+* interfaces and where clauses
* '#auto' return type
* Macros and paste-able code blocks
* overloaded procedures can have polymorphic and overloaded procedures as overloads. This makes them MUCH more powerful.
* operator overloading is now done as a top level declaration handled through the entity
system, instead of relying on creating a procedure. This lets you use an existing
procedure as an operator overload. Take a look at '#operator ==' in string.onyx.
+* easier to write types in expressions
* Tons of internal code cleanup relating to structures and code generation.
Bug fixes:
NODE(PolySolution) \
NODE(SolidifiedFunction) \
NODE(PolyProc) \
+ NODE(PolyQuery) \
\
NODE(Note) \
NODE(CallSite) \
Ast_Kind_Function,
Ast_Kind_Overloaded_Function,
Ast_Kind_Polymorphic_Proc,
+ Ast_Kind_Polymorph_Query,
Ast_Kind_Interface,
Ast_Kind_Constraint,
Ast_Kind_Constraint_Sentinel,
// Expression Nodes
struct AstNamedValue { AstTyped_base; AstTyped* value; };
-struct AstBinaryOp { AstTyped_base; BinaryOp operation; AstTyped *left, *right; };
struct AstUnaryOp { AstTyped_base; UnaryOp operation; AstTyped *expr; };
struct AstNumLit { AstTyped_base; union { i32 i; i64 l; f32 f; f64 d; } value; };
struct AstStrLit { AstTyped_base; u64 addr; u64 length; };
struct AstLocal { AstTyped_base; };
-struct AstAddressOf { AstTyped_base; AstTyped *expr; };
struct AstDereference { AstTyped_base; AstTyped *expr; };
struct AstSizeOf { AstTyped_base; AstType *so_ast_type; Type *so_type; u64 size; };
struct AstAlignOf { AstTyped_base; AstType *ao_ast_type; Type *ao_type; u64 alignment; };
+struct AstBinaryOp {
+ AstTyped_base;
+ BinaryOp operation;
+ AstTyped *left, *right;
+
+ Arguments *overload_args; // This is set of the binary operator is attempted to be overloaded
+ // but isnt successful yet.
+ AstBinaryOp *potential_substitute;
+};
+struct AstAddressOf {
+ AstTyped_base;
+ AstTyped *expr;
+
+ AstBinaryOp *potential_substitute;
+};
struct AstArgument {
AstTyped_base;
BinaryOp __unused_operation; // This will be set to Binary_Op_Subscript
AstTyped *addr;
AstTyped *expr;
+
+ Arguments *overload_args; // This is set of the binary operator is attempted to be overloaded
+ // but isnt successful yet.
+ AstBinaryOp *potential_substitute;
+
u64 elem_size;
};
struct AstFieldAccess {
PSK_Value,
} PolySolutionKind;
+typedef enum PolyProcLookupMethod {
+ PPLM_By_Arguments,
+ PPLM_By_Function_Type,
+} PolyProcLookupMethod;
+
struct AstPolyParam {
PolyParamKind kind;
AstFunction* base_func;
bh_table(AstSolidifiedFunction) concrete_funcs;
+ bh_imap active_queries;
+};
+
+struct AstPolyQuery {
+ AstNode_base;
+
+ AstPolyProc *proc;
+ PolyProcLookupMethod pp_lookup;
+ ptr given;
+ OnyxToken *error_loc;
+
+ bh_arr(AstPolySolution) slns;
+
+ AstFunction *function_header;
+
+ b32 error_on_fail : 1; // Whether or not to report errors on failing to match.
+ b32 successful_symres : 1; // If something successful happened in symbol resolution
};
Entity_Type_Interface,
Entity_Type_Constraint_Check,
Entity_Type_Polymorphic_Proc,
+ Entity_Type_Polymorph_Query,
Entity_Type_Macro,
Entity_Type_Foreign_Function_Header,
Entity_Type_Temp_Function_Header, // Same as a Function_Header, except it disappears after it checks completely.
AstEnumType *enum_type;
AstMemRes *mem_res;
AstPolyProc *poly_proc;
+ AstPolyQuery *poly_query;
AstMacro *macro;
AstUse *use;
AstInterface *interface;
b32 static_if_resolution(AstIf* static_if);
-typedef enum PolyProcLookupMethod {
- PPLM_By_Arguments,
- PPLM_By_Function_Type,
-} PolyProcLookupMethod;
+void insert_poly_sln_into_scope(Scope* scope, AstPolySolution *sln);
+TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFunction *func, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg);
AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn);
AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
"FUNCTION",
"OVERLOADED_FUNCTION",
"POLYMORPHIC PROC",
+ "POLYMORPH QUERY",
"INTERFACE",
"CONSTRAINT",
"CONSTRAINT SENTITEL",
"Interface",
"Constraint Check",
"Polymorphic Proc",
+ "Polymorph Query",
"Macro",
"Foreign_Function Header",
"Temporary Function Header",
if (node->kind == Ast_Kind_Polymorphic_Proc) {
AstFunction* func = polymorphic_proc_lookup((AstPolyProc *) node, PPLM_By_Function_Type, type, node->token);
if (func == NULL) return TYPE_MATCH_FAILED;
-
- // FIXME: This is incorrect. It should actually yield and not return a failure.
if (func == (AstFunction *) &node_that_signals_a_yield) return TYPE_MATCH_YIELD;
*pnode = (AstTyped *) func;
CheckStatus check_do_block(AstDoBlock** pdoblock);
CheckStatus check_constraint(AstConstraint *constraint);
CheckStatus check_constraint_context(ConstraintContext *cc, OnyxFilePos pos);
+CheckStatus check_polyquery(AstPolyQuery *query);
// HACK HACK HACK
b32 expression_types_must_be_known = 0;
static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop, AstTyped* third_argument) {
if (bh_arr_length(operator_overloads[binop->operation]) == 0) return NULL;
- u8 value_buffer[sizeof(bh__arr) + sizeof(AstTyped *) * 3];
- Arguments args = ((Arguments) { NULL, NULL });
- args.values = (AstTyped **) &value_buffer[sizeof(bh__arr)];
- bh_arr_set_length(args.values, third_argument ? 3 : 2);
+ if (binop->overload_args == NULL) {
+ binop->overload_args = bh_alloc_item(context.ast_alloc, Arguments);
+ bh_arr_new(context.ast_alloc, binop->overload_args->values, 3);
+ bh_arr_set_length(binop->overload_args->values, third_argument ? 3 : 2);
- if (binop_is_assignment(binop->operation)) {
- args.values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
+ if (binop_is_assignment(binop->operation)) {
+ binop->overload_args->values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
- u32 current_all_checks_are_final = all_checks_are_final;
- all_checks_are_final = 0;
- u32 current_checking_level_store = current_checking_level;
- CheckStatus cs = check_address_of((AstAddressOf **) &args.values[0]);
- current_checking_level = current_checking_level_store;
- all_checks_are_final = current_all_checks_are_final;
+ u32 current_all_checks_are_final = all_checks_are_final;
+ all_checks_are_final = 0;
+ u32 current_checking_level_store = current_checking_level;
+ CheckStatus cs = check_address_of((AstAddressOf **) &binop->overload_args->values[0]);
+ current_checking_level = current_checking_level_store;
+ all_checks_are_final = current_all_checks_are_final;
- if (cs == Check_Yield_Macro) return (AstCall *) &node_that_signals_a_yield;
- if (cs == Check_Error) return NULL;
+ if (cs == Check_Yield_Macro) return (AstCall *) &node_that_signals_a_yield;
+ if (cs == Check_Error) return NULL;
- args.values[0] = (AstTyped *) make_argument(context.ast_alloc, args.values[0]);
+ binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->overload_args->values[0]);
- } else {
- args.values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->left);
- }
+ } else {
+ binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->left);
+ }
- args.values[1] = (AstTyped *) make_argument(context.ast_alloc, binop->right);
- if (third_argument != NULL) args.values[2] = (AstTyped *) make_argument(context.ast_alloc, third_argument);
+ binop->overload_args->values[1] = (AstTyped *) make_argument(context.ast_alloc, binop->right);
+ if (third_argument != NULL) binop->overload_args->values[2] = (AstTyped *) make_argument(context.ast_alloc, third_argument);
+ }
- AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], &args);
+ AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], binop->overload_args);
if (overload == NULL || overload == (AstTyped *) &node_that_signals_a_yield) return (AstCall *) overload;
AstCall* implicit_call = onyx_ast_node_new(context.ast_alloc, sizeof(AstCall), Ast_Kind_Call);
implicit_call->callee = overload;
implicit_call->va_kind = VA_Kind_Not_VA;
- arguments_clone(&implicit_call->args, &args);
+ arguments_clone(&implicit_call->args, binop->overload_args);
return implicit_call;
}
if (binop->operation == Binary_Op_Assign && binop->left->kind == Ast_Kind_Subscript && bh_arr_length(operator_overloads[Binary_Op_Subscript_Equals]) > 0) {
AstSubscript* sub = (AstSubscript *) binop->left;
- u32 current_checking_level_store = current_checking_level;
- CHECK(expression, &sub->addr);
- CHECK(expression, &sub->expr);
- CHECK(expression, &binop->right);
- current_checking_level = current_checking_level_store;
-
- AstBinaryOp op;
- op.kind = Ast_Kind_Binary_Op;
- op.token = binop->token;
- op.operation = Binary_Op_Subscript_Equals;
- op.left = ((AstSubscript *) binop->left)->addr;
- op.right = ((AstSubscript *) binop->left)->expr;
-
- AstCall* call = binaryop_try_operator_overload(&op, binop->right);
- if (call == (AstCall *) &node_that_signals_a_yield) YIELD(op.token->pos, "Waiting on potential operator overload.");
+ if (binop->potential_substitute == NULL) {
+ u32 current_checking_level_store = current_checking_level;
+ CHECK(expression, &sub->addr);
+ CHECK(expression, &sub->expr);
+ CHECK(expression, &binop->right);
+ current_checking_level = current_checking_level_store;
+
+ AstBinaryOp *op = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
+ op->token = binop->token;
+ op->operation = Binary_Op_Subscript_Equals;
+ op->left = ((AstSubscript *) binop->left)->addr;
+ op->right = ((AstSubscript *) binop->left)->expr;
+
+ binop->potential_substitute = op;
+ }
+
+ AstCall* call = binaryop_try_operator_overload(binop->potential_substitute, binop->right);
+ if (call == (AstCall *) &node_that_signals_a_yield) YIELD(binop->token->pos, "Waiting on potential operator overload.");
if (call != NULL) {
call->next = binop->next;
*(AstCall **) pbinop = call;
CheckStatus check_range_literal(AstRangeLiteral** prange) {
AstRangeLiteral* range = *prange;
+ if (range->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
CHECK(expression, &range->low);
CHECK(expression, &range->high);
range->step = *smem.initial_value;
}
+ range->flags |= Ast_Flag_Has_Been_Checked;
return Check_Success;
}
AstTyped* expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
if (expr->kind == Ast_Kind_Subscript && bh_arr_length(operator_overloads[Binary_Op_Ptr_Subscript]) > 0) {
- CHECK(expression, &((AstSubscript *) expr)->addr);
- CHECK(expression, &((AstSubscript *) expr)->expr);
+ if (aof->potential_substitute == NULL) {
+ CHECK(expression, &((AstSubscript *) expr)->addr);
+ CHECK(expression, &((AstSubscript *) expr)->expr);
- AstBinaryOp op;
- op.kind = Ast_Kind_Binary_Op;
- op.operation = Binary_Op_Ptr_Subscript;
- op.left = ((AstSubscript *) expr)->addr;
- op.right = ((AstSubscript *) expr)->expr;
- op.token = aof->token;
+ AstBinaryOp *op = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
+ op->operation = Binary_Op_Ptr_Subscript;
+ op->left = ((AstSubscript *) expr)->addr;
+ op->right = ((AstSubscript *) expr)->expr;
+ op->token = aof->token;
- AstCall* call = binaryop_try_operator_overload(&op, NULL);
+ aof->potential_substitute = op;
+ }
+
+ AstCall* call = binaryop_try_operator_overload(aof->potential_substitute, NULL);
if (call == (AstCall *) &node_that_signals_a_yield) YIELD(aof->token->pos, "Waiting for operator overload to possibly resolve.");
if (call != NULL) {
call->next = aof->next;
}
}
+CheckStatus check_polyquery(AstPolyQuery *query) {
+ if (query->function_header->scope == NULL)
+ query->function_header->scope = scope_create(context.ast_alloc, query->proc->poly_scope, query->token->pos);
+
+ CheckStatus header_check = check_temp_function_header(query->function_header);
+ if (header_check == Check_Return_To_Symres) return Check_Return_To_Symres;
+
+ b32 solved_something = 0;
+ i32 solved_count = 0;
+ char *err_msg = NULL;
+ bh_arr_each(AstPolyParam, param, query->proc->poly_params) {
+ AstPolySolution sln;
+ bh_arr_each(AstPolySolution, solved_sln, query->slns) {
+ if (token_equals(param->poly_sym->token, solved_sln->poly_sym->token)) {
+ goto poly_query_done;
+ }
+ }
+
+ // CLEANUP: I think this can go away because it is already done in polymorph.c
+ bh_arr_each(AstPolySolution, known_sln, query->proc->known_slns) {
+ if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) {
+ sln = *known_sln;
+ goto poly_var_solved;
+ }
+ }
+
+ TypeMatch result = find_polymorphic_sln(&sln, param, query->function_header, query->pp_lookup, query->given, &err_msg);
+
+ switch (result) {
+ case TYPE_MATCH_SUCCESS:
+ goto poly_var_solved;
+
+ case TYPE_MATCH_YIELD:
+ case TYPE_MATCH_FAILED: {
+ if (query->successful_symres) continue;
+
+ if (query->error_on_fail || context.cycle_detected) {
+ onyx_report_error(query->token->pos, "Error solving for polymorphic variable '%b'.", param->poly_sym->token->text, param->poly_sym->token->length);
+ if (err_msg != NULL) onyx_report_error(query->token->pos, "%s", err_msg);
+ if (query->error_loc) onyx_report_error(query->error_loc->pos, "Here is where the call is located."); // :ErrorMessage
+ }
+
+ return Check_Failed;
+ }
+ }
+
+poly_var_solved:
+ solved_something = 1;
+ bh_arr_push(query->slns, sln);
+ insert_poly_sln_into_scope(query->function_header->scope, &sln);
+
+poly_query_done:
+ solved_count += 1;
+ }
+
+ if (solved_count != bh_arr_length(query->proc->poly_params)) {
+ if (solved_something || query->successful_symres) {
+ return Check_Return_To_Symres;
+ } else {
+ return Check_Failed;
+ }
+ }
+
+ return Check_Complete;
+}
+
CheckStatus check_node(AstNode* node) {
switch (node->kind) {
case Ast_Kind_Function: return check_function((AstFunction *) node);
case Entity_Type_Static_If: cs = check_static_if(ent->static_if); break;
case Entity_Type_Macro: cs = check_macro(ent->macro); break;
case Entity_Type_Constraint_Check: cs = check_constraint(ent->constraint); break;
+ case Entity_Type_Polymorph_Query: cs = check_polyquery(ent->poly_query); break;
case Entity_Type_Expression:
cs = check_expression(&ent->expr);
break;
}
+ case Ast_Kind_Polymorph_Query: {
+ ent.type = Entity_Type_Polymorph_Query;
+ ent.state = Entity_State_Check_Types;
+ ent.poly_query = (AstPolyQuery *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
case Ast_Kind_Static_If: {
ent.state = Entity_State_Resolve_Symbols;
ent.type = Entity_Type_Static_If;
static void ensure_polyproc_cache_is_created(AstPolyProc* pp) {
if (pp->concrete_funcs == NULL) {
bh_table_init(global_heap_allocator, pp->concrete_funcs, 16);
+
+ bh_imap_init(&pp->active_queries, global_heap_allocator, 31);
}
}
-static void insert_poly_slns_into_scope(Scope* scope, bh_arr(AstPolySolution) slns) {
- bh_arr_each(AstPolySolution, sln, slns) {
- AstNode *node = NULL;
+void insert_poly_sln_into_scope(Scope* scope, AstPolySolution *sln) {
+ AstNode *node = NULL;
- switch (sln->kind) {
- case PSK_Type:
- node = onyx_ast_node_new(context.ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
- ((AstTypeRawAlias *) node)->token = sln->poly_sym->token;
- ((AstTypeRawAlias *) node)->to = sln->type;
- break;
+ switch (sln->kind) {
+ case PSK_Type:
+ node = onyx_ast_node_new(context.ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
+ ((AstTypeRawAlias *) node)->token = sln->poly_sym->token;
+ ((AstTypeRawAlias *) node)->to = sln->type;
+ break;
- case PSK_Value:
- // CLEANUP: Maybe clone this?
- assert(sln->value->flags & Ast_Flag_Comptime);
- node = (AstNode *) sln->value;
- break;
- }
+ case PSK_Value:
+ // CLEANUP: Maybe clone this?
+ assert(sln->value->flags & Ast_Flag_Comptime);
+ node = (AstNode *) sln->value;
+ break;
+ }
+
+ symbol_introduce(scope, sln->poly_sym->token, node);
+}
- symbol_introduce(scope, sln->poly_sym->token, node);
+static void insert_poly_slns_into_scope(Scope* scope, bh_arr(AstPolySolution) slns) {
+ bh_arr_each(AstPolySolution, sln, slns) {
+ insert_poly_sln_into_scope(scope, sln);
}
}
// and solve for the argument that matches the parameter. This is needed because polymorphic
// procedure resolution has to happen before the named arguments are placed in their correct
// positions.
-static AstTyped* lookup_param_in_arguments(AstPolyProc* pp, AstPolyParam* param, Arguments* args, char** err_msg) {
+static AstTyped* lookup_param_in_arguments(AstFunction* func, AstPolyParam* param, Arguments* args, char** err_msg) {
bh_arr(AstTyped *) arg_arr = args->values;
bh_arr(AstNamedValue *) named_values = args->named_values;
// NOTE: This check is safe because currently the arguments given without a name
// always map to the beginning indidies of the argument array.
if (param->idx >= (u64) bh_arr_length(arg_arr)) {
- OnyxToken* param_name = pp->base_func->params[param->idx].local->token;
+ OnyxToken* param_name = func->params[param->idx].local->token;
bh_arr_each(AstNamedValue *, named_value, named_values) {
if (token_equals(param_name, (*named_value)->token)) {
// information. It is asssumed that the "param" is of kind PPK_Poly_Type. This function uses
// either the arguments provided, or a function type to compare against to pattern match for
// the type that the parameter but be.
-static void solve_for_polymorphic_param_type(PolySolveResult* resolved, AstPolyProc* pp, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+static void solve_for_polymorphic_param_type(PolySolveResult* resolved, AstFunction* func, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
Type* actual_type = NULL;
switch (pp_lookup) {
case PPLM_By_Arguments: {
Arguments* args = (Arguments *) actual;
- AstTyped* typed_param = lookup_param_in_arguments(pp, param, args, err_msg);
+ AstTyped* typed_param = lookup_param_in_arguments(func, param, args, err_msg);
if (typed_param == NULL) return;
actual_type = resolve_expression_type(typed_param);
// CLEANUP: This function is kind of gross at the moment, because it handles different cases for
// the argument kind. When type expressions (type_expr) become first-class types in the type
// system, this code should be able to be a lot cleaner.
-static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstPolyProc* pp, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstFunction* func, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
if (pp_lookup != PPLM_By_Arguments) {
*err_msg = "Function type cannot be used to solved for baked parameter value.";
return;
}
Arguments* args = (Arguments *) actual;
- AstTyped* value = lookup_param_in_arguments(pp, param, args, err_msg);
+ AstTyped* value = lookup_param_in_arguments(func, param, args, err_msg);
if (value == NULL) return;
// HACK: Storing the original value because if this was an AstArgument, we need to flag
value = ((AstArgument *) value)->value;
}
- if (param->type_expr == (AstType *) &basic_type_type_expr) {
+ AstType *param_type_expr = func->params[param->idx].local->type_node;
+ if (param_type_expr == (AstType *) &basic_type_type_expr) {
if (!node_is_type((AstNode *) value)) {
if (err_msg) *err_msg = "Expected type expression.";
return;
}
if (param->type == NULL)
- param->type = type_build_from_ast(context.ast_alloc, param->type_expr);
- assert(param->type);
+ param->type = type_build_from_ast(context.ast_alloc, param_type_expr);
+
+ if (param->type == NULL) {
+ flag_to_yield = 1;
+ *err_msg = "Waiting to know type for polymorphic value.";
+ return;
+ }
AstTyped* value_to_use = value;
if (value->kind == Ast_Kind_Macro) {
if (tm == TYPE_MATCH_FAILED) {
if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator,
"The procedure '%s' expects a value of type '%s' for baked %d%s parameter, got '%s'.",
- get_function_name(pp->base_func),
+ get_function_name(func),
type_get_name(param->type),
param->idx + 1,
bh_num_suffix(param->idx + 1),
}
}
+TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFunction *func, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+ // NOTE: Solve for the polymorphic parameter's value
+ PolySolveResult resolved = { PSK_Undefined };
+ switch (param->kind) {
+ case PPK_Poly_Type: solve_for_polymorphic_param_type (&resolved, func, param, pp_lookup, actual, err_msg); break;
+ case PPK_Baked_Value: solve_for_polymorphic_param_value(&resolved, func, param, pp_lookup, actual, err_msg); break;
+
+ default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug.";
+ }
+
+ if (flag_to_yield) {
+ flag_to_yield = 0;
+ return TYPE_MATCH_YIELD;
+ }
+
+ switch (resolved.kind) {
+ case PSK_Type:
+ out->kind = PSK_Type;
+ out->poly_sym = param->poly_sym;
+ out->type = resolved.actual;
+ return TYPE_MATCH_SUCCESS;
+
+ case PSK_Value:
+ out->kind = PSK_Value;
+ out->poly_sym = param->poly_sym;
+ out->value = resolved.value;
+ return TYPE_MATCH_SUCCESS;
+
+ case PSK_Undefined:
+ default:
+ // NOTE: If no error message has been assigned to why this polymorphic parameter
+ // resolution was unsuccessful, provide a basic dummy one.
+ if (err_msg && *err_msg == NULL)
+ *err_msg = bh_aprintf(global_scratch_allocator,
+ "Unable to solve for polymorphic variable '%b'.",
+ param->poly_sym->token->text,
+ param->poly_sym->token->length);
+
+ out->kind = PSK_Undefined;
+ return TYPE_MATCH_FAILED;
+ }
+}
+
// NOTE: The job of this function is to take a polymorphic procedure, as well as a method of
// solving for the polymorphic variables, in order to return an array of the solutions for all
// of the polymorphic variables.
-static bh_arr(AstPolySolution) find_polymorphic_slns(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+static bh_arr(AstPolySolution) find_polymorphic_slns(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken *tkn, b32 necessary) {
+ if (bh_imap_has(&pp->active_queries, (u64) actual)) {
+ AstPolyQuery *query = (AstPolyQuery *) bh_imap_get(&pp->active_queries, (u64) actual);
+ assert(query->kind == Ast_Kind_Polymorph_Query);
+ assert(query->entity);
+
+ if (query->entity->state == Entity_State_Finalized) return query->slns;
+ if (query->entity->state == Entity_State_Failed) return NULL;
+
+ flag_to_yield = 1;
+ return NULL;
+ }
+
bh_arr(AstPolySolution) slns = NULL;
bh_arr_new(global_heap_allocator, slns, bh_arr_length(pp->poly_params));
// empty and these solutions will be used.
bh_arr_each(AstPolySolution, known_sln, pp->known_slns) bh_arr_push(slns, *known_sln);
- bh_arr_each(AstPolyParam, param, pp->poly_params) {
-
- // NOTE: First check to see if this polymorphic variable was already specified in the
- // known solutions.
- b32 already_solved = 0;
- bh_arr_each(AstPolySolution, known_sln, pp->known_slns) {
- if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) {
- already_solved = 1;
- break;
- }
- }
- if (already_solved) continue;
-
- // NOTE: Solve for the polymorphic parameter's value
- PolySolveResult resolved = { PSK_Undefined };
- switch (param->kind) {
- case PPK_Poly_Type: solve_for_polymorphic_param_type (&resolved, pp, param, pp_lookup, actual, err_msg); break;
- case PPK_Baked_Value: solve_for_polymorphic_param_value(&resolved, pp, param, pp_lookup, actual, err_msg); break;
-
- default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug.";
- }
-
- if (flag_to_yield) goto sln_not_found;
-
- switch (resolved.kind) {
- case PSK_Undefined:
- // NOTE: If no error message has been assigned to why this polymorphic parameter
- // resolution was unsuccessful, provide a basic dummy one.
- if (err_msg && *err_msg == NULL)
- *err_msg = bh_aprintf(global_scratch_allocator,
- "Unable to solve for polymorphic variable '%b'.",
- param->poly_sym->token->text,
- param->poly_sym->token->length);
-
- goto sln_not_found;
-
- case PSK_Type:
- bh_arr_push(slns, ((AstPolySolution) {
- .kind = PSK_Type,
- .poly_sym = param->poly_sym,
- .type = resolved.actual,
- }));
- break;
-
- case PSK_Value:
- bh_arr_push(slns, ((AstPolySolution) {
- .kind = PSK_Value,
- .poly_sym = param->poly_sym,
- .value = resolved.value,
- }));
- break;
- }
- }
-
- return slns;
-
- sln_not_found:
- bh_arr_free(slns);
+ AstPolyQuery *query = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyQuery), Ast_Kind_Polymorph_Query);
+ query->token = pp->token;
+ query->proc = pp;
+ query->pp_lookup = pp_lookup;
+ query->given = actual;
+ query->error_loc = tkn;
+ query->slns = slns;
+ query->function_header = clone_function_header(context.ast_alloc, pp->base_func);
+ query->function_header->flags |= Ast_Flag_Header_Check_No_Error;
+ query->error_on_fail = necessary;
+ query->successful_symres = 1;
+
+ bh_imap_put(&pp->active_queries, (u64) actual, (u64) query);
+ add_entities_for_node(NULL, (AstNode *) query, NULL, NULL);
+
+ flag_to_yield = 1;
return NULL;
}
AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn) {
ensure_polyproc_cache_is_created(pp);
- char *err_msg = NULL;
- bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, &err_msg);
+ bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, tkn, 1);
if (slns == NULL) {
if (flag_to_yield) {
flag_to_yield = 0;
return (AstFunction *) &node_that_signals_a_yield;
}
- if (err_msg != NULL) onyx_report_error(tkn->pos, err_msg);
- else onyx_report_error(tkn->pos, "Some kind of error occured when generating a polymorphic procedure. You hopefully will not see this");
-
return NULL;
}
AstFunction* result = polymorphic_proc_solidify(pp, slns, tkn);
-
- bh_arr_free(slns);
return result;
}
}
AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual) {
- bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, NULL);
+ ensure_polyproc_cache_is_created(pp);
+ bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, NULL, 0);
+ if (flag_to_yield) {
+ flag_to_yield = 0;
+ return (AstFunction *) &node_that_signals_a_yield;
+ }
if (slns == NULL) return NULL;
ensure_polyproc_cache_is_created(pp);
// Variables used during the symbol resolution phase.
static Scope* curr_scope = NULL;
static b32 report_unresolved_symbols = 1;
+static b32 resolved_a_symbol = 0;
// Everything related to waiting on is imcomplete at the moment.
static Entity* waiting_on = NULL;
static SymresStatus symres_static_if(AstIf* static_if);
static SymresStatus symres_macro(AstMacro* macro);
static SymresStatus symres_constraint(AstConstraint* constraint);
+static SymresStatus symres_polyquery(AstPolyQuery *query);
static void scope_enter(Scope* new_scope) {
curr_scope = new_scope;
} else {
*symbol_node = res;
+ resolved_a_symbol = 1;
}
return Symres_Success;
static SymresStatus symres_polyproc(AstPolyProc* pp) {
pp->flags |= Ast_Flag_Comptime;
pp->poly_scope = curr_scope;
-
- bh_arr_each(AstPolyParam, param, pp->poly_params) {
- if (param->kind != PPK_Baked_Value) continue;
-
- // FIX: Looking up symbols immediately in the type of the baked value does not always work
- // because I think the following should be possible:
- //
- // baked_proc :: (x: $T, $f: (T) -> T) -> T ...
- //
- // The type of 'f' depends on resolving the value for the polyvar 'T'.
- SYMRES(type, ¶m->type_expr);
- }
-
return Symres_Success;
}
return Symres_Success;
}
+static SymresStatus symres_polyquery(AstPolyQuery *query) {
+ query->successful_symres = 0;
+
+ if (query->function_header->scope == NULL)
+ query->function_header->scope = scope_create(context.ast_alloc, query->proc->poly_scope, query->token->pos);
+
+ scope_enter(query->function_header->scope);
+
+ u32 idx = 0;
+ bh_arr_each(AstParam, param, query->function_header->params) {
+ bh_arr_each(AstPolyParam, pp, query->proc->poly_params) {
+ if (pp->kind == PPK_Baked_Value && pp->idx == idx) goto skip_introducing_symbol;
+ }
+ symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
+
+ skip_introducing_symbol:
+ if (param->local->type_node != NULL) {
+ resolved_a_symbol = 0;
+ symres_type(¶m->local->type_node);
+ onyx_clear_errors();
+
+ if (resolved_a_symbol) query->successful_symres = 1;
+ }
+
+ idx++;
+ }
+
+ scope_leave();
+ return Symres_Success;
+}
+
void symres_entity(Entity* ent) {
Scope* old_scope = NULL;
if (ent->scope) {
case Entity_Type_Process_Directive: ss = symres_process_directive((AstNode *) ent->expr); break;
case Entity_Type_Macro: ss = symres_macro(ent->macro); break;
case Entity_Type_Constraint_Check: ss = symres_constraint(ent->constraint); break;
+ case Entity_Type_Polymorph_Query: ss = symres_polyquery(ent->poly_query); break;
default: break;
}
case Ast_Kind_Array_Type: {
AstArrayType* a_node = (AstArrayType *) type_node;
+ Type *elem_type = type_build_from_ast(alloc, a_node->elem);
+ if (elem_type == NULL) return NULL;
+
Type* a_type = type_create(Type_Kind_Array, alloc, 0);
- a_type->Array.elem = type_build_from_ast(alloc, a_node->elem);
+ a_type->Array.elem = elem_type;
u32 count = 0;
if (a_node->count_expr) {
a_node->count_expr = ((AstUnaryOp *) a_node)->expr;
}
- resolve_expression_type((AstTyped *) a_node->count_expr);
+ resolve_expression_type(a_node->count_expr);
// NOTE: Currently, the count_expr has to be an I32 literal
if (a_node->count_expr->type->kind != Type_Kind_Basic
return NULL;
}
- count = get_expression_integer_value((AstTyped *) a_node->count_expr);
+ count = get_expression_integer_value(a_node->count_expr);
}
a_type->Array.count = count;
AstPolyProc* pp = (AstPolyProc *) macro->body;
ensure_polyproc_cache_is_created(pp);
- char* err_msg=NULL;
- bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, PPLM_By_Arguments, args, &err_msg);
+ bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, PPLM_By_Arguments, args, callsite, error_if_failed);
if (slns == NULL) {
if (flag_to_yield) {
return (AstFunction *) &node_that_signals_a_yield;
}
- if (callsite) onyx_report_error(callsite->pos, err_msg);
-
return NULL;
}