diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 781203564..3e1810e15 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -684,6 +684,52 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E entity->Procedure.generated_from_polymorphic = proc_info->generated_from_polymorphic; } + if (base_entity->Procedure.deferred_procedure.entity != nullptr) { + DeferredProcedure dp = base_entity->Procedure.deferred_procedure; + Entity *dst = dp.entity; + // NOTE(bill): Skip self-deferral and chaining (both invalid, reported by check_deferred_procedures). + // handling them here would otherwise recurse through find_or_generate_polymorphic_procedure. + if (dst == nullptr || dst == base_entity || dst->Procedure.deferred_procedure.entity != nullptr) { + // Leave this instantiation without a deferred procedure + // The error is reported during validation + } else if (!is_type_polymorphic(dst->type)) { + entity->Procedure.deferred_procedure = dp; + } else { + bool by_ptr = dp.kind == DeferredProcedure_in_by_ptr || dp.kind == DeferredProcedure_out_by_ptr || dp.kind == DeferredProcedure_in_out_by_ptr; + bool use_in = dp.kind == DeferredProcedure_in || dp.kind == DeferredProcedure_in_by_ptr || dp.kind == DeferredProcedure_in_out || dp.kind == DeferredProcedure_in_out_by_ptr; + bool use_out = dp.kind == DeferredProcedure_out || dp.kind == DeferredProcedure_out_by_ptr || dp.kind == DeferredProcedure_in_out || dp.kind == DeferredProcedure_in_out_by_ptr; + + auto dst_ops = array_make(heap_allocator(), 0, operands.count + 4); + defer (array_free(&dst_ops)); + + if (use_in) { + for (Operand const &o : operands) { + Operand po = o; + if (by_ptr && po.type != nullptr) { + po.type = alloc_type_pointer(po.type); + } + array_add(&dst_ops, po); + } + } + if (use_out) { + Type *res = base_type(final_proc_type)->Proc.results; + if (res != nullptr && res->kind == Type_Tuple) { + for (Entity *rv : res->Tuple.variables) { + Operand po = {Addressing_Value}; + po.type = by_ptr ? alloc_type_pointer(rv->type) : rv->type; + po.expr = ident; + array_add(&dst_ops, po); + } + } + } + PolyProcData dpd = {}; + if (find_or_generate_polymorphic_procedure(&nctx, dst, nullptr, &dst_ops, poly_def_node, &dpd)) { + entity->Procedure.deferred_procedure.kind = dp.kind; + entity->Procedure.deferred_procedure.entity = dpd.gen_entity; + } + } + } + // NOTE(bill): Check the newly generated procedure body check_procedure_later(nctx.checker, proc_info); diff --git a/src/checker.cpp b/src/checker.cpp index fa95f763d..6273a8f7e 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -6964,8 +6964,14 @@ gb_internal void check_deferred_procedures(Checker *c) { continue; } - if (is_type_polymorphic(src->type) || is_type_polymorphic(dst->type)) { - error(src->token, "'%s' cannot be used with a polymorphic procedure", attribute); + bool src_poly = is_type_polymorphic(src->type); + bool dst_poly = is_type_polymorphic(dst->type); + if (dst_poly && !src_poly) { + error(src->token, "A polymorphic deferred procedure '%.*s' requires the initial procedure '%.*s' to be polymorphic as well", LIT(dst->token.string), LIT(src->token.string)); + continue; + } + if (dst_poly && dst_kind == DeferredProcedure_none) { + error(src->token, "'deferred_none' cannot be used with a polymorphic deferred procedure, as it has no inputs from which to determine its polymorphic types"); continue; } @@ -7027,7 +7033,7 @@ gb_internal void check_deferred_procedures(Checker *c) { GB_ASSERT(src_params->kind == Type_Tuple); GB_ASSERT(dst_params->kind == Type_Tuple); - if (are_types_identical(src_params, dst_params)) { + if (src_poly || dst_poly || are_types_identical(src_params, dst_params)) { // Okay! } else { gbString s = type_to_string(src_params); @@ -7057,7 +7063,7 @@ gb_internal void check_deferred_procedures(Checker *c) { GB_ASSERT(src_results->kind == Type_Tuple); GB_ASSERT(dst_params->kind == Type_Tuple); - if (are_types_identical(src_results, dst_params)) { + if (src_poly || dst_poly || are_types_identical(src_results, dst_params)) { // Okay! } else { gbString s = type_to_string(src_results); @@ -7114,7 +7120,7 @@ gb_internal void check_deferred_procedures(Checker *c) { GB_ASSERT(offset == len); - if (are_types_identical(tsrc, dst_params)) { + if (src_poly || dst_poly || are_types_identical(tsrc, dst_params)) { // Okay! } else { gbString s = type_to_string(tsrc);