-
-
Notifications
You must be signed in to change notification settings - Fork 419
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inference of lambda type and array element type from an antecedent (RFC 45). #2168
Changes from 2 commits
6a44eaa
460d5e4
0c22363
39333d8
070cf82
858beef
14f909e
7e89179
943c1ba
5c12f9d
92eeec2
6cc7428
aa5b46a
638ed60
750e155
4fb0253
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,14 +10,259 @@ | |
#include "../pass/refer.h" | ||
#include "../type/alias.h" | ||
#include "../type/assemble.h" | ||
#include "../type/reify.h" | ||
#include "../type/subtype.h" | ||
#include "../type/lookup.h" | ||
#include "ponyassert.h" | ||
|
||
static ast_t* build_array_type(ast_t* scope, ast_t* elem_type, token_id cap) | ||
{ | ||
elem_type = ast_dup(elem_type); | ||
|
||
BUILD(array_type, elem_type, | ||
NODE(TK_NOMINAL, | ||
ID("$0") | ||
ID("Array") | ||
NODE(TK_TYPEARGS, TREE(elem_type)) | ||
NODE(cap) | ||
NODE(TK_EPHEMERAL) | ||
NODE(TK_NONE))); | ||
|
||
// Link the nominal type to the entity definition for Array. | ||
ast_setdata(array_type, ast_get(scope, stringtab("Array"), NULL)); | ||
|
||
return array_type; | ||
} | ||
|
||
static void find_possible_element_types(pass_opt_t* opt, ast_t* ast, | ||
astlist_t** list) | ||
{ | ||
switch(ast_id(ast)) | ||
{ | ||
case TK_NOMINAL: | ||
{ | ||
AST_GET_CHILDREN(ast, package, name, typeargs, cap, eph); | ||
|
||
// If it's an actual Array type, note it as a possibility and move on. | ||
if(stringtab("Array") == ast_name(name)) | ||
{ | ||
*list = astlist_push(*list, ast_child(typeargs)); | ||
return; | ||
} | ||
|
||
// Otherwise, an Array-matching type must be an interface. | ||
ast_t* def = (ast_t*)ast_data(ast); | ||
if((def == NULL) || (ast_id(def) != TK_INTERFACE)) | ||
return; | ||
|
||
// The interface must have an apply method for us to find it. | ||
ast_t* apply = ast_get(def, stringtab("apply"), NULL); | ||
if((apply == NULL) || (ast_id(apply) != TK_FUN)) | ||
return; | ||
|
||
// The apply method must match the signature we're expecting. | ||
AST_GET_CHILDREN(apply, receiver_cap, apply_name, type_params, params, | ||
ret_type, question); | ||
if((ast_id(receiver_cap) != TK_BOX) || | ||
(ast_id(type_params) != TK_NONE) || | ||
(ast_childcount(params) != 1) || | ||
(ast_id(question) != TK_QUESTION)) | ||
return; | ||
|
||
ast_t* param = ast_child(params); | ||
ast_t* param_type = ast_childidx(param, 1); | ||
if(ast_name(ast_childidx(param_type, 1)) != stringtab("USize")) | ||
return; | ||
|
||
// Based on the apply method we try to figure out the element type. | ||
ast_t* elem_type = ret_type; | ||
|
||
ast_t* typeparams = ast_childidx(def, 1); | ||
if(ast_id(typeparams) == TK_TYPEPARAMS) | ||
elem_type = reify(ret_type, typeparams, typeargs, opt, true); | ||
|
||
if((ast_id(elem_type) == TK_ARROW) && | ||
(ast_id(ast_child(elem_type)) == TK_THISTYPE)) | ||
elem_type = ast_childidx(elem_type, 1); | ||
|
||
// Construct a guess of the corresponding Array type. | ||
// Use iso^ so that the object cap isn't a concern for subtype checking. | ||
ast_t* array_type = build_array_type(ast, elem_type, TK_ISO); | ||
|
||
// The guess type must be a subtype of the interface type. | ||
if(!is_subtype(array_type, ast, NULL, opt)) | ||
{ | ||
ast_free_unattached(array_type); | ||
return; | ||
} | ||
|
||
ast_free_unattached(array_type); | ||
|
||
// Note this as a possible element type and move on. | ||
*list = astlist_push(*list, elem_type); | ||
} | ||
|
||
case TK_ARROW: | ||
find_possible_element_types(opt, ast_childidx(ast, 1), list); | ||
return; | ||
|
||
case TK_TYPEPARAMREF: | ||
{ | ||
ast_t* def = (ast_t*)ast_data(ast); | ||
pony_assert(ast_id(def) == TK_TYPEPARAM); | ||
find_possible_element_types(opt, ast_childidx(def, 1), list); | ||
return; | ||
} | ||
|
||
case TK_UNIONTYPE: | ||
case TK_ISECTTYPE: | ||
{ | ||
for(ast_t* c = ast_child(ast); c != NULL; c = ast_sibling(c)) | ||
find_possible_element_types(opt, c, list); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd feel more comfortable with a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call. Fixed in 943c1ba. |
||
|
||
default: | ||
break; | ||
} | ||
} | ||
|
||
static bool infer_element_type(pass_opt_t* opt, ast_t* ast, | ||
ast_t** type_spec_p, ast_t* antecedent_type) | ||
{ | ||
// List the element types of all array-matching types in the antecedent type. | ||
astlist_t* possible_element_types = NULL; | ||
find_possible_element_types(opt, antecedent_type, &possible_element_types); | ||
|
||
// If there's more than one possible element type, test against the elements, | ||
// creating a new list containing only the possibilities that are supertypes. | ||
if(astlist_length(possible_element_types) > 1) | ||
{ | ||
astlist_t* new_list = NULL; | ||
|
||
astlist_t* cursor = possible_element_types; | ||
for(; cursor != NULL; cursor = astlist_next(cursor)) | ||
{ | ||
bool supertype_of_all = true; | ||
ast_t* elem = ast_child(ast_childidx(ast, 1)); | ||
for(; elem != NULL; elem = ast_sibling(elem)) | ||
{ | ||
// Catch up the elem to the expr pass, so we can get its type. | ||
if(ast_visit(&elem, pass_pre_expr, pass_expr, opt, PASS_EXPR) != AST_OK) | ||
return false; | ||
|
||
ast_t* elem_type = ast_type(elem); | ||
if(is_typecheck_error(elem_type) || ast_id(elem_type) == TK_LITERAL) | ||
break; | ||
|
||
ast_t* a_type = alias(elem_type); | ||
|
||
if(!is_subtype(a_type, astlist_data(cursor), NULL, opt)) | ||
supertype_of_all = false; | ||
|
||
ast_free_unattached(a_type); | ||
} | ||
|
||
if(supertype_of_all) | ||
new_list = astlist_push(new_list, astlist_data(cursor)); | ||
} | ||
|
||
astlist_free(possible_element_types); | ||
possible_element_types = new_list; | ||
} | ||
|
||
// If there's still more than one possible element type, choose the most | ||
// specific type (removing types that are supertypes of one or more others). | ||
if(astlist_length(possible_element_types) > 1) | ||
{ | ||
astlist_t* new_list = NULL; | ||
|
||
astlist_t* super_cursor = possible_element_types; | ||
for(; super_cursor != NULL; super_cursor = astlist_next(super_cursor)) | ||
{ | ||
bool supertype_of_any = false; | ||
|
||
astlist_t* sub_cursor = possible_element_types; | ||
for(; sub_cursor != NULL; sub_cursor = astlist_next(sub_cursor)) | ||
{ | ||
if((sub_cursor != super_cursor) && is_subtype(astlist_data(sub_cursor), | ||
astlist_data(super_cursor), NULL, opt)) | ||
supertype_of_any = true; | ||
} | ||
|
||
if(!supertype_of_any) | ||
new_list = astlist_push(new_list, astlist_data(super_cursor)); | ||
} | ||
|
||
astlist_free(possible_element_types); | ||
possible_element_types = new_list; | ||
} | ||
|
||
// If there's exactly one possible element type remaining, use it. | ||
if(astlist_length(possible_element_types) == 1) | ||
ast_replace(type_spec_p, astlist_data(possible_element_types)); | ||
|
||
return true; | ||
} | ||
|
||
ast_result_t expr_pre_array(pass_opt_t* opt, ast_t** astp) | ||
{ | ||
ast_t* ast = *astp; | ||
|
||
pony_assert(ast_id(ast) == TK_ARRAY); | ||
AST_GET_CHILDREN(ast, type_spec, elements); | ||
|
||
// Try to find an antecedent type, or bail out if none was found. | ||
bool is_recovered = false; | ||
ast_t* antecedent_type = find_antecedent_type(opt, ast, &is_recovered); | ||
if(antecedent_type == NULL) | ||
return AST_OK; | ||
|
||
// If we don't have an explicit element type, try to infer it. | ||
if(ast_id(type_spec) == TK_NONE) | ||
{ | ||
if(!infer_element_type(opt, ast, &type_spec, antecedent_type)) | ||
return AST_ERROR; | ||
} | ||
|
||
// If we still don't have an element type, bail out. | ||
if(ast_id(type_spec) == TK_NONE) | ||
return AST_OK; | ||
|
||
// If there is no recover statement between the antecedent type and here, | ||
// and if the array literal is not a subtype of the antecedent type, | ||
// but would be if the object cap were ignored, then recover it. | ||
ast_t* array_type = build_array_type(ast, type_spec, TK_REF); | ||
if(!is_recovered && !is_subtype(array_type, antecedent_type, NULL, opt) && | ||
is_subtype_ignore_cap(array_type, antecedent_type, NULL, opt)) | ||
{ | ||
ast_free_unattached(array_type); | ||
|
||
BUILD(recover, ast, | ||
NODE(TK_RECOVER, | ||
NODE(TK_ISO) | ||
NODE(TK_SEQ, TREE(ast)))); | ||
|
||
ast_replace(astp, recover); | ||
|
||
// Run the expr pass on this recover block. | ||
if(ast_visit(astp, pass_pre_expr, pass_expr, opt, PASS_EXPR) != AST_OK) | ||
return AST_ERROR; | ||
|
||
// We've already processed the expr pass for the array, so ignore it now. | ||
return AST_IGNORE; | ||
} | ||
|
||
ast_free_unattached(array_type); | ||
return AST_OK; | ||
} | ||
|
||
bool expr_array(pass_opt_t* opt, ast_t** astp) | ||
{ | ||
ast_t* ast = *astp; | ||
ast_t* type = NULL; | ||
bool told_type = false; | ||
|
||
pony_assert(ast_id(ast) == TK_ARRAY); | ||
AST_GET_CHILDREN(ast, type_spec, elements); | ||
size_t size = ast_childcount(elements); | ||
|
||
|
@@ -27,6 +272,13 @@ bool expr_array(pass_opt_t* opt, ast_t** astp) | |
told_type = true; | ||
} | ||
|
||
if(!told_type && (ast_childcount(elements) == 0)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call. Fixed in 943c1ba. |
||
{ | ||
ast_error(opt->check.errors, ast, "an empty array literal must specify " | ||
"the element type or it must be inferable from context"); | ||
return false; | ||
} | ||
|
||
for(ast_t* ele = ast_child(elements); ele != NULL; ele = ast_sibling(ele)) | ||
{ | ||
if(ast_checkflag(ele, AST_FLAG_JUMPS_AWAY)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a particular reason to use this new function instead of
type_builtin_args
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know about that function.
In 943c1ba I refactored
build_array_type
to usetype_builtin_args
as part of its implementation.