From b055373a1408224c8207c053d3a30b2fee7d5d6b Mon Sep 17 00:00:00 2001 From: sylvanc Date: Tue, 7 Mar 2017 08:58:50 +0000 Subject: [PATCH 01/10] move object literal handling from sugar to expr pass --- src/libponyc/ast/frame.c | 14 +- src/libponyc/ast/parser.c | 1 + src/libponyc/expr/lambda.c | 313 +++++++++++++++++++++++++++++++++++++ src/libponyc/expr/lambda.h | 2 + src/libponyc/pass/expr.c | 5 + src/libponyc/pass/pass.c | 18 ++- src/libponyc/pass/sugar.c | 191 ---------------------- 7 files changed, 342 insertions(+), 202 deletions(-) diff --git a/src/libponyc/ast/frame.c b/src/libponyc/ast/frame.c index f46cfc2d25..d0fea21fdb 100644 --- a/src/libponyc/ast/frame.c +++ b/src/libponyc/ast/frame.c @@ -27,13 +27,12 @@ bool frame_push(typecheck_t* t, ast_t* ast) if(ast == NULL) { - typecheck_frame_t* prev = t->frame; - - pop = push_frame(t); - memset(t->frame, 0, sizeof(typecheck_frame_t)); - t->frame->prev = prev; + typecheck_frame_t* f = POOL_ALLOC(typecheck_frame_t); + memset(f, 0, sizeof(typecheck_frame_t)); + f->prev = t->frame; + t->frame = f; - return pop; + return true; } switch(ast_id(ast)) @@ -252,8 +251,7 @@ bool frame_push(typecheck_t* t, ast_t* ast) pop = push_frame(t); t->frame->ifdef_cond = cond; t->frame->ifdef_clause = body; - } - else if(else_clause == ast) { + } else if(else_clause == ast) { pop = push_frame(t); t->frame->ifdef_cond = else_cond; t->frame->ifdef_clause = else_clause; diff --git a/src/libponyc/ast/parser.c b/src/libponyc/ast/parser.c index bca58bd62a..3c56a4cf78 100644 --- a/src/libponyc/ast/parser.c +++ b/src/libponyc/ast/parser.c @@ -306,6 +306,7 @@ DEF(object); IF(TK_IS, RULE("provided type", provides)); RULE("object member", members); TERMINATE("object literal", TK_END); + SET_CHILD_FLAG(2, AST_FLAG_PRESERVE); // Members DONE(); // ID [COLON type] [ASSIGN infix] diff --git a/src/libponyc/expr/lambda.c b/src/libponyc/expr/lambda.c index 5729715c00..25fc72d0d4 100644 --- a/src/libponyc/expr/lambda.c +++ b/src/libponyc/expr/lambda.c @@ -8,6 +8,7 @@ #include "../pass/syntax.h" #include "../type/alias.h" #include "../type/sanitise.h" +#include "../pkg/package.h" #include "ponyassert.h" @@ -142,6 +143,7 @@ bool expr_lambda(pass_opt_t* opt, ast_t** astp) NONE)); // Guard ast_list_append(members, &last_member, apply); + ast_setflag(members, AST_FLAG_PRESERVE); printbuf_t* buf = printbuf_new(); printbuf(buf, "{("); @@ -182,3 +184,314 @@ bool expr_lambda(pass_opt_t* opt, ast_t** astp) return ast_passes_subtree(astp, opt, PASS_EXPR); } + + +static bool capture_free_variable(pass_opt_t* opt, ast_t* ctx, ast_t* def, + ast_t* ast) +{ + const char* name = ast_name(ast_child(ast)); + + sym_status_t status; + ast_t* refdef = ast_get(ast, name, &status); + + if(refdef != NULL) + return true; + + // TODO: capture + refdef = ast_get(ctx, name, &status); + + if(refdef == NULL) + { + ast_error(opt->check.errors, ast, + "cannot capture \"%s\", variable not defined", name); + return false; + } + + if(!def_before_use(opt, refdef, ctx, name)) + return false; + + token_id def_id = ast_id(refdef); + + if(def_id != TK_ID && def_id != TK_FVAR && def_id != TK_FLET && + def_id != TK_PARAM) + { + ast_error(opt->check.errors, ast, "cannot capture \"%s\", can only " + "capture fields, parameters and local variables", name); + return false; + } + + // TODO: check if we've already captured it + + // TODO: add as a field, add as a constructor parameter, init field from + // parameter in constructor, add at the call site + ast_t* type = alias(ast_type(refdef)); + + if(is_typecheck_error(type)) + return false; + + type = sanitise_type(type); + (void)def; + // ast_t* p_id = ast_from_string(ast, package_hygienic_id(&opt->check)); + + // BUILD(field, def, + // NODE(TK_FVAR, + // ID(name) + // TREE(type) + // NONE)); + + // BUILD(param, def, + // NODE(TK_PARAM, + // TREE(p_id) + // TREE(type) + // NONE)); + + // BUILD(arg, ctx, NODE(TK_REFERENCE, ID(name))); + + // BUILD(assign, def, + // NODE(TK_ASSIGN, + // NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) + // NODE(TK_REFERENCE, ID(name)))); + + return true; +} + + +static bool capture_free_variables(pass_opt_t* opt, ast_t* ctx, ast_t* def, + ast_t* ast) +{ + bool ok = true; + + if(ast_id(ast) == TK_REFERENCE) + { + if(!capture_free_variable(opt, ctx, def, ast)) + ok = false; + } + + for(ast_t* p = ast_child(ast); p != NULL; p = ast_sibling(p)) + { + if(!capture_free_variables(opt, ctx, def, p)) + ok = false; + } + + return ok; +} + + +bool expr_object(pass_opt_t* opt, ast_t** astp) +{ + ast_t* ast = *astp; + bool ok = true; + + AST_GET_CHILDREN(ast, cap, provides, members); + ast_clearflag(members, AST_FLAG_PRESERVE); + + ast_t* annotation = ast_consumeannotation(ast); + const char* c_id = package_hygienic_id(&opt->check); + + ast_t* t_params; + ast_t* t_args; + collect_type_params(ast, &t_params, &t_args); + + const char* nice_id = (const char*)ast_data(ast); + if(nice_id == NULL) + nice_id = "object literal"; + + // Create a new anonymous type. + BUILD(def, ast, + NODE(TK_CLASS, AST_SCOPE + ANNOTATE(annotation) + NICE_ID(c_id, nice_id) + TREE(t_params) + NONE + TREE(provides) + NODE(TK_MEMBERS) + NONE + NONE)); + + // We will have a create method in the type. + BUILD(create, members, + NODE(TK_NEW, AST_SCOPE + NONE + ID("create") + NONE + NONE + NONE + NONE + NODE(TK_SEQ) + NONE + NONE)); + + BUILD(type_ref, ast, NODE(TK_REFERENCE, ID(c_id))); + + if(ast_id(t_args) != TK_NONE) + { + // Need to add type args to our type reference + BUILD(t, ast, NODE(TK_QUALIFY, TREE(type_ref) TREE(t_args))); + type_ref = t; + } + + ast_free_unattached(t_args); + + // We will replace object..end with $0.create(...) + BUILD(call, ast, + NODE(TK_CALL, + NONE + NONE + NODE(TK_DOT, + TREE(type_ref) + ID("create")))); + + ast_t* create_params = ast_childidx(create, 3); + ast_t* create_body = ast_childidx(create, 6); + ast_t* call_args = ast_child(call); + ast_t* class_members = ast_childidx(def, 4); + ast_t* member = ast_child(members); + + bool has_fields = false; + bool has_behaviours = false; + + while(member != NULL) + { + switch(ast_id(member)) + { + case TK_FVAR: + case TK_FLET: + case TK_EMBED: + { + AST_GET_CHILDREN(member, id, type, init); + ast_t* p_id = ast_from_string(id, package_hygienic_id(&opt->check)); + + // The field is: var/let/embed id: type + BUILD(field, member, + NODE(ast_id(member), + TREE(id) + TREE(type) + NONE)); + + // The param is: $0: type + BUILD(param, member, + NODE(TK_PARAM, + TREE(p_id) + TREE(type) + NONE)); + + // The arg is: $seq init + BUILD(arg, init, + NODE(TK_SEQ, + TREE(init))); + + // The body of create contains: id = consume $0 + BUILD(assign, init, + NODE(TK_ASSIGN, + NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) + NODE(TK_REFERENCE, TREE(id)))); + + ast_setid(create_params, TK_PARAMS); + ast_setid(call_args, TK_POSITIONALARGS); + + ast_append(class_members, field); + ast_append(create_params, param); + ast_append(create_body, assign); + ast_append(call_args, arg); + + has_fields = true; + break; + } + + case TK_BE: + // If we have behaviours, we must be an actor. + ast_append(class_members, member); + has_behaviours = true; + break; + + default: + // Keep all the methods as they are. + ast_append(class_members, member); + break; + } + + member = ast_sibling(member); + } + + if(!has_fields) + { + // End the constructor with 'true', since it has no parameters. + BUILD(true_node, ast, NODE(TK_TRUE)); + ast_append(create_body, true_node); + } + + // Handle capability and whether the anonymous type is a class, primitive or + // actor. + token_id cap_id = ast_id(cap); + + if(has_behaviours) + { + // Change the type to an actor. + ast_setid(def, TK_ACTOR); + + if(cap_id != TK_NONE && cap_id != TK_TAG) + { + ast_error(opt->check.errors, cap, "object literals with behaviours are " + "actors and so must have tag capability"); + ok = false; + } + } + else if(!has_fields && (cap_id == TK_NONE || cap_id == TK_TAG || + cap_id == TK_BOX || cap_id == TK_VAL)) + { + // Change the type from a class to a primitive. + ast_setid(def, TK_PRIMITIVE); + } + else + { + // Type is a class, set the create capability as specified + ast_setid(ast_child(create), cap_id); + } + + // Add the create function at the end. + ast_append(class_members, create); + + // Replace object..end with $0.create(...) + ast_t* module = ast_nearest(ast, TK_MODULE); + ast_replace(astp, call); + + // Add new type to current module and bring it up to date with passes. + ast_append(module, def); + + // Turn any free variables into fields. + if(!ast_passes_subtree(&def, opt, PASS_SCOPE)) + return false; + + members = ast_childidx(def, 4); + + for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) + { + switch(ast_id(p)) + { + case TK_FUN: + case TK_BE: + if(!capture_free_variables(opt, *astp, def, p)) + ok = false; + break; + + default: {} + } + } + + // Type check the anonymous type. + // TODO: failing on a reused hygienic variable when recalcing scope + ast_clear(def); + ast_resetpass(def); + + if(!ast_passes_type(&def, opt)) + return false; + + // Catch up passes + if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK) + return false; + + if(!ast_passes_subtree(astp, opt, PASS_EXPR)) + return false; + + return ok; +} diff --git a/src/libponyc/expr/lambda.h b/src/libponyc/expr/lambda.h index 73bc16b79d..f14544a324 100644 --- a/src/libponyc/expr/lambda.h +++ b/src/libponyc/expr/lambda.h @@ -9,6 +9,8 @@ PONY_EXTERN_C_BEGIN bool expr_lambda(pass_opt_t* opt, ast_t** astp); +bool expr_object(pass_opt_t* opt, ast_t** astp); + PONY_EXTERN_C_END #endif diff --git a/src/libponyc/pass/expr.c b/src/libponyc/pass/expr.c index 65e5474eb5..d059bfc2c9 100644 --- a/src/libponyc/pass/expr.c +++ b/src/libponyc/pass/expr.c @@ -292,6 +292,11 @@ ast_result_t pass_expr(ast_t** astp, pass_opt_t* options) case TK_ADDRESS: r = expr_addressof(options, ast); break; case TK_DIGESTOF: r = expr_digestof(options, ast); break; + case TK_OBJECT: + if(!expr_object(options, astp)) + return AST_FATAL; + break; + case TK_LAMBDA: if(!expr_lambda(options, astp)) return AST_FATAL; diff --git a/src/libponyc/pass/pass.c b/src/libponyc/pass/pass.c index 73f7ed3867..f7cf023f10 100644 --- a/src/libponyc/pass/pass.c +++ b/src/libponyc/pass/pass.c @@ -103,9 +103,9 @@ void pass_opt_done(pass_opt_t* options) errors_free(options->check.errors); options->check.errors = NULL; - // Pop all the typechecker frames. - while(options->check.frame != NULL) - frame_pop(&options->check); + // Pop the initial typechecker frame. + frame_pop(&options->check); + pony_assert(options->check.frame == NULL); if(options->print_stats) { @@ -341,6 +341,10 @@ ast_result_t ast_visit(ast_t** ast, ast_visit_t pre, ast_visit_t post, case AST_FATAL: record_ast_pass(*ast, pass); + + if(pop) + frame_pop(t); + return AST_FATAL; } } @@ -367,6 +371,10 @@ ast_result_t ast_visit(ast_t** ast, ast_visit_t pre, ast_visit_t post, case AST_FATAL: record_ast_pass(*ast, pass); + + if(pop) + frame_pop(t); + return AST_FATAL; } @@ -388,6 +396,10 @@ ast_result_t ast_visit(ast_t** ast, ast_visit_t pre, ast_visit_t post, case AST_FATAL: record_ast_pass(*ast, pass); + + if(pop) + frame_pop(t); + return AST_FATAL; } } diff --git a/src/libponyc/pass/sugar.c b/src/libponyc/pass/sugar.c index 56a79a69c0..97886bc648 100644 --- a/src/libponyc/pass/sugar.c +++ b/src/libponyc/pass/sugar.c @@ -749,196 +749,6 @@ static ast_result_t sugar_update(ast_t** astp) } -static ast_result_t sugar_object(pass_opt_t* opt, ast_t** astp) -{ - ast_t* ast = *astp; - ast_result_t r = AST_OK; - - AST_GET_CHILDREN(ast, cap, provides, members); - ast_t* annotation = ast_consumeannotation(ast); - const char* c_id = package_hygienic_id(&opt->check); - - ast_t* t_params; - ast_t* t_args; - collect_type_params(ast, &t_params, &t_args); - - const char* nice_id = (const char*)ast_data(ast); - if(nice_id == NULL) - nice_id = "object literal"; - - // Create a new anonymous type. - BUILD(def, ast, - NODE(TK_CLASS, AST_SCOPE - ANNOTATE(annotation) - NICE_ID(c_id, nice_id) - TREE(t_params) - NONE - TREE(provides) - NODE(TK_MEMBERS) - NONE - NONE)); - - // We will have a create method in the type. - BUILD(create, members, - NODE(TK_NEW, AST_SCOPE - NONE - ID("create") - NONE - NONE - NONE - NONE - NODE(TK_SEQ) - NONE - NONE)); - - BUILD(type_ref, ast, NODE(TK_REFERENCE, ID(c_id))); - - if(ast_id(t_args) != TK_NONE) - { - // Need to add type args to our type reference - BUILD(t, ast, NODE(TK_QUALIFY, TREE(type_ref) TREE(t_args))); - type_ref = t; - } - - ast_free_unattached(t_args); - - // We will replace object..end with $0.create(...) - BUILD(call, ast, - NODE(TK_CALL, - NONE - NONE - NODE(TK_DOT, - TREE(type_ref) - ID("create")))); - - ast_t* create_params = ast_childidx(create, 3); - ast_t* create_body = ast_childidx(create, 6); - ast_t* call_args = ast_child(call); - ast_t* class_members = ast_childidx(def, 4); - ast_t* member = ast_child(members); - - bool has_fields = false; - bool has_behaviours = false; - - while(member != NULL) - { - switch(ast_id(member)) - { - case TK_FVAR: - case TK_FLET: - case TK_EMBED: - { - AST_GET_CHILDREN(member, id, type, init); - ast_t* p_id = ast_from_string(id, package_hygienic_id(&opt->check)); - - // The field is: var/let/embed id: type - BUILD(field, member, - NODE(ast_id(member), - TREE(id) - TREE(type) - NONE)); - - // The param is: $0: type - BUILD(param, member, - NODE(TK_PARAM, - TREE(p_id) - TREE(type) - NONE)); - - // The arg is: $seq init - BUILD(arg, init, - NODE(TK_SEQ, - TREE(init))); - - // The body of create contains: id = consume $0 - BUILD(assign, init, - NODE(TK_ASSIGN, - NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) - NODE(TK_REFERENCE, TREE(id)))); - - ast_setid(create_params, TK_PARAMS); - ast_setid(call_args, TK_POSITIONALARGS); - - ast_append(class_members, field); - ast_append(create_params, param); - ast_append(create_body, assign); - ast_append(call_args, arg); - - has_fields = true; - break; - } - - case TK_BE: - // If we have behaviours, we must be an actor. - ast_append(class_members, member); - has_behaviours = true; - break; - - default: - // Keep all the methods as they are. - ast_append(class_members, member); - break; - } - - member = ast_sibling(member); - } - - if(!has_fields) - { - // End the constructor with 'true', since it has no parameters. - BUILD(true_node, ast, NODE(TK_TRUE)); - ast_append(create_body, true_node); - } - - // Handle capability and whether the anonymous type is a class, primitive or - // actor. - token_id cap_id = ast_id(cap); - - if(has_behaviours) - { - // Change the type to an actor. - ast_setid(def, TK_ACTOR); - - if(cap_id != TK_NONE && cap_id != TK_TAG) - { - ast_error(opt->check.errors, cap, "object literals with behaviours are " - "actors and so must have tag capability"); - r = AST_ERROR; - } - } - else if(!has_fields && (cap_id == TK_NONE || cap_id == TK_TAG || - cap_id == TK_BOX || cap_id == TK_VAL)) - { - // Change the type from a class to a primitive. - ast_setid(def, TK_PRIMITIVE); - } - else - { - // Type is a class, set the create capability as specified - ast_setid(ast_child(create), cap_id); - } - - // Add the create function at the end. - ast_append(class_members, create); - - // Replace object..end with $0.create(...) - ast_t* module = ast_nearest(ast, TK_MODULE); - ast_replace(astp, call); - - // Add new type to current module and bring it up to date with passes. - ast_append(module, def); - - if(!ast_passes_type(&def, opt)) - return AST_FATAL; - - // Sugar the call. - if(!ast_passes_subtree(astp, opt, PASS_SUGAR)) - return AST_FATAL; - - return r; -} - - static void add_as_type(pass_opt_t* opt, ast_t* type, ast_t* pattern, ast_t* body) { @@ -1425,7 +1235,6 @@ ast_result_t pass_sugar(ast_t** astp, pass_opt_t* options) case TK_WITH: return sugar_with(options, astp); case TK_CASE: return sugar_case(options, ast); case TK_ASSIGN: return sugar_update(astp); - case TK_OBJECT: return sugar_object(options, astp); case TK_AS: return sugar_as(options, astp); case TK_PLUS: return sugar_binop(astp, "add"); case TK_MINUS: return sugar_binop(astp, "sub"); From 0f2b7c7fcfc323c2341a7628253ea506d85d6129 Mon Sep 17 00:00:00 2001 From: sylvanc Date: Tue, 7 Mar 2017 08:59:52 +0000 Subject: [PATCH 02/10] comment out capture code --- src/libponyc/expr/lambda.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libponyc/expr/lambda.c b/src/libponyc/expr/lambda.c index 25fc72d0d4..8683ef7aaa 100644 --- a/src/libponyc/expr/lambda.c +++ b/src/libponyc/expr/lambda.c @@ -458,6 +458,7 @@ bool expr_object(pass_opt_t* opt, ast_t** astp) // Add new type to current module and bring it up to date with passes. ast_append(module, def); +#if 0 // Turn any free variables into fields. if(!ast_passes_subtree(&def, opt, PASS_SCOPE)) return false; @@ -482,6 +483,7 @@ bool expr_object(pass_opt_t* opt, ast_t** astp) // TODO: failing on a reused hygienic variable when recalcing scope ast_clear(def); ast_resetpass(def); +#endif if(!ast_passes_type(&def, opt)) return false; From 879b332cc47b53530a5ae1da31abaed5122d86b0 Mon Sep 17 00:00:00 2001 From: Sylvan Clebsch Date: Tue, 7 Mar 2017 17:27:36 +0000 Subject: [PATCH 03/10] parameterised type aliases aren't generating correct refcaps for typeparamrefs --- src/libponyc/ast/ast.c | 27 +++++++++---- src/libponyc/ast/ast.h | 2 +- src/libponyc/ast/astbuild.h | 2 +- src/libponyc/ast/parser.c | 2 + src/libponyc/expr/lambda.c | 75 ++++++++++++++++++++++--------------- src/libponyc/pass/flatten.c | 3 -- src/libponyc/pass/pass.c | 4 +- src/libponyc/pass/pass.h | 2 +- src/libponyc/pass/sugar.c | 2 +- 9 files changed, 73 insertions(+), 46 deletions(-) diff --git a/src/libponyc/ast/ast.c b/src/libponyc/ast/ast.c index ffc6873ac1..240400d0cc 100644 --- a/src/libponyc/ast/ast.c +++ b/src/libponyc/ast/ast.c @@ -566,16 +566,22 @@ void ast_clearflag(ast_t* ast, uint32_t flag) ast->flags &= ~flag; } -void ast_resetpass(ast_t* ast) +void ast_resetpass(ast_t* ast, uint32_t flag) { + pony_assert((flag & AST_FLAG_PASS_MASK) == flag); + if(ast == NULL) return; + if(ast_checkflag(ast, AST_FLAG_PRESERVE)) + return; + ast_clearflag(ast, AST_FLAG_PASS_MASK); - ast_resetpass(ast->type); + ast_setflag(ast, flag); + ast_resetpass(ast->type, flag); for(ast_t* p = ast_child(ast); p != NULL; p = ast_sibling(p)) - ast_resetpass(p); + ast_resetpass(p, flag); } const char* ast_get_print(ast_t* ast) @@ -910,17 +916,24 @@ bool ast_set(ast_t* ast, const char* name, ast_t* value, sym_status_t status, while(ast->symtab == NULL) ast = ast->parent; + ast_t* find; + if(allow_shadowing) { // Only check the local scope. - if(symtab_find_case(ast->symtab, name, NULL) != NULL) - return false; + find = symtab_find_case(ast->symtab, name, NULL); } else { // Check the local scope and all parent scopes. - if(ast_get_case(ast, name, NULL) != NULL) - return false; + find = ast_get_case(ast, name, NULL); } + // Pretend we succeeded if the mapping in the symbol table wouldn't change. + if(find == value) + return true; + + if(find != NULL) + return false; + return symtab_add(ast->symtab, name, value, status); } diff --git a/src/libponyc/ast/ast.h b/src/libponyc/ast/ast.h index eb0b84b19a..d3d0b07c4b 100644 --- a/src/libponyc/ast/ast.h +++ b/src/libponyc/ast/ast.h @@ -79,7 +79,7 @@ void ast_inheritflags(ast_t* ast); int ast_checkflag(ast_t* ast, uint32_t flag); void ast_setflag(ast_t* ast, uint32_t flag); void ast_clearflag(ast_t* ast, uint32_t flag); -void ast_resetpass(ast_t* ast); +void ast_resetpass(ast_t *ast, uint32_t flag); const char* ast_get_print(ast_t* ast); const char* ast_name(ast_t* ast); diff --git a/src/libponyc/ast/astbuild.h b/src/libponyc/ast/astbuild.h index e4bf9ef9e7..444cbfc715 100644 --- a/src/libponyc/ast/astbuild.h +++ b/src/libponyc/ast/astbuild.h @@ -69,7 +69,7 @@ #define TREE_CLEAR_PASS(tree) \ { \ if(ast_parent(tree) != NULL) tree = ast_dup(tree); \ - ast_resetpass(tree); \ + ast_resetpass(tree. 0); \ TREE(tree); \ } diff --git a/src/libponyc/ast/parser.c b/src/libponyc/ast/parser.c index 3c56a4cf78..dd2b8313a8 100644 --- a/src/libponyc/ast/parser.c +++ b/src/libponyc/ast/parser.c @@ -306,6 +306,8 @@ DEF(object); IF(TK_IS, RULE("provided type", provides)); RULE("object member", members); TERMINATE("object literal", TK_END); + SET_CHILD_FLAG(0, AST_FLAG_PRESERVE); // Cap + SET_CHILD_FLAG(1, AST_FLAG_PRESERVE); // Provides SET_CHILD_FLAG(2, AST_FLAG_PRESERVE); // Members DONE(); diff --git a/src/libponyc/expr/lambda.c b/src/libponyc/expr/lambda.c index 8683ef7aaa..cfe0a1c42b 100644 --- a/src/libponyc/expr/lambda.c +++ b/src/libponyc/expr/lambda.c @@ -197,7 +197,6 @@ static bool capture_free_variable(pass_opt_t* opt, ast_t* ctx, ast_t* def, if(refdef != NULL) return true; - // TODO: capture refdef = ast_get(ctx, name, &status); if(refdef == NULL) @@ -230,27 +229,31 @@ static bool capture_free_variable(pass_opt_t* opt, ast_t* ctx, ast_t* def, return false; type = sanitise_type(type); + (void)def; - // ast_t* p_id = ast_from_string(ast, package_hygienic_id(&opt->check)); - // BUILD(field, def, - // NODE(TK_FVAR, - // ID(name) - // TREE(type) - // NONE)); + printf("CAPTURE %s\n", name); + + ast_t* p_id = ast_from_string(ast, package_hygienic_id(&opt->check)); + + BUILD(field, def, + NODE(TK_FVAR, + ID(name) + TREE(type) + NONE)); - // BUILD(param, def, - // NODE(TK_PARAM, - // TREE(p_id) - // TREE(type) - // NONE)); + BUILD(param, def, + NODE(TK_PARAM, + TREE(p_id) + TREE(type) + NONE)); - // BUILD(arg, ctx, NODE(TK_REFERENCE, ID(name))); + BUILD(arg, ctx, NODE(TK_REFERENCE, ID(name))); - // BUILD(assign, def, - // NODE(TK_ASSIGN, - // NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) - // NODE(TK_REFERENCE, ID(name)))); + BUILD(assign, def, + NODE(TK_ASSIGN, + NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) + NODE(TK_REFERENCE, ID(name)))); return true; } @@ -259,18 +262,24 @@ static bool capture_free_variable(pass_opt_t* opt, ast_t* ctx, ast_t* def, static bool capture_free_variables(pass_opt_t* opt, ast_t* ctx, ast_t* def, ast_t* ast) { + // Skip preserved ASTs. + if(ast_checkflag(ast, AST_FLAG_PRESERVE)) + return true; + bool ok = true; if(ast_id(ast) == TK_REFERENCE) { + // Try to capture references. if(!capture_free_variable(opt, ctx, def, ast)) ok = false; - } - - for(ast_t* p = ast_child(ast); p != NULL; p = ast_sibling(p)) - { - if(!capture_free_variables(opt, ctx, def, p)) - ok = false; + } else { + // Proceed down through all child ASTs. + for(ast_t* p = ast_child(ast); p != NULL; p = ast_sibling(p)) + { + if(!capture_free_variables(opt, ctx, def, p)) + ok = false; + } } return ok; @@ -283,6 +292,8 @@ bool expr_object(pass_opt_t* opt, ast_t** astp) bool ok = true; AST_GET_CHILDREN(ast, cap, provides, members); + ast_clearflag(cap, AST_FLAG_PRESERVE); + ast_clearflag(provides, AST_FLAG_PRESERVE); ast_clearflag(members, AST_FLAG_PRESERVE); ast_t* annotation = ast_consumeannotation(ast); @@ -293,6 +304,7 @@ bool expr_object(pass_opt_t* opt, ast_t** astp) collect_type_params(ast, &t_params, &t_args); const char* nice_id = (const char*)ast_data(ast); + if(nice_id == NULL) nice_id = "object literal"; @@ -458,9 +470,8 @@ bool expr_object(pass_opt_t* opt, ast_t** astp) // Add new type to current module and bring it up to date with passes. ast_append(module, def); -#if 0 // Turn any free variables into fields. - if(!ast_passes_subtree(&def, opt, PASS_SCOPE)) + if(!ast_passes_type(&def, opt, PASS_SCOPE)) return false; members = ast_childidx(def, 4); @@ -471,21 +482,25 @@ bool expr_object(pass_opt_t* opt, ast_t** astp) { case TK_FUN: case TK_BE: + { if(!capture_free_variables(opt, *astp, def, p)) ok = false; break; + } default: {} } } - // Type check the anonymous type. - // TODO: failing on a reused hygienic variable when recalcing scope + // Reset the scope and rewind the pass. ast_clear(def); - ast_resetpass(def); -#endif + ast_resetpass(def, PASS_SUGAR); - if(!ast_passes_type(&def, opt)) + // TODO: something about parameterised type aliases goes wrong + // nothing to do with the scope stuff above + + // Type check the anonymous type. + if(!ast_passes_type(&def, opt, PASS_EXPR)) return false; // Catch up passes diff --git a/src/libponyc/pass/flatten.c b/src/libponyc/pass/flatten.c index f28effab5d..beee1f9f0a 100644 --- a/src/libponyc/pass/flatten.c +++ b/src/libponyc/pass/flatten.c @@ -328,9 +328,6 @@ ast_result_t pass_flatten(ast_t** astp, pass_opt_t* options) case TK_INTERFACE: return flatten_provides_list(options, ast, 3); - case TK_OBJECT: - return flatten_provides_list(options, ast, 0); - default: {} } diff --git a/src/libponyc/pass/pass.c b/src/libponyc/pass/pass.c index f7cf023f10..db868ffb6d 100644 --- a/src/libponyc/pass/pass.c +++ b/src/libponyc/pass/pass.c @@ -250,7 +250,7 @@ bool ast_passes_program(ast_t* ast, pass_opt_t* options) } -bool ast_passes_type(ast_t** astp, pass_opt_t* options) +bool ast_passes_type(ast_t** astp, pass_opt_t* options, pass_id last_pass) { ast_t* ast = *astp; @@ -267,7 +267,7 @@ bool ast_passes_type(ast_t** astp, pass_opt_t* options) frame_push(&options->check, package); frame_push(&options->check, module); - bool ok = ast_passes(astp, options, options->program_pass); + bool ok = ast_passes(astp, options, last_pass); frame_pop(&options->check); frame_pop(&options->check); diff --git a/src/libponyc/pass/pass.h b/src/libponyc/pass/pass.h index f80cc4381e..54b96baf4e 100644 --- a/src/libponyc/pass/pass.h +++ b/src/libponyc/pass/pass.h @@ -269,7 +269,7 @@ bool ast_passes_program(ast_t* program, pass_opt_t* options); * been through some passes and so may not be in a state that the current pass * expects. */ -bool ast_passes_type(ast_t** astp, pass_opt_t* options); +bool ast_passes_type(ast_t** astp, pass_opt_t* options, pass_id last_pass); /** Catch up the given sub-AST to the specified pass. * Returns true on success, false on failure. diff --git a/src/libponyc/pass/sugar.c b/src/libponyc/pass/sugar.c index 97886bc648..d65c1d17c5 100644 --- a/src/libponyc/pass/sugar.c +++ b/src/libponyc/pass/sugar.c @@ -1118,7 +1118,7 @@ static ast_result_t sugar_lambdatype(pass_opt_t* opt, ast_t** astp) ast_t* module = ast_nearest(ast, TK_MODULE); ast_append(module, def); - if(!ast_passes_type(&def, opt)) + if(!ast_passes_type(&def, opt, opt->program_pass)) return AST_FATAL; // Sugar the call. From a407728cd8d44f2e3bb7c8e16b7a94d465fe5da6 Mon Sep 17 00:00:00 2001 From: sylvanc Date: Tue, 7 Mar 2017 23:27:51 +0000 Subject: [PATCH 04/10] reset the pass when resolving type aliases --- src/libponyc/pass/names.c | 4 + test/libponyc/annotations.cc | 19 +--- test/libponyc/sugar.cc | 167 ----------------------------------- 3 files changed, 7 insertions(+), 183 deletions(-) diff --git a/src/libponyc/pass/names.c b/src/libponyc/pass/names.c index 9e13f0ca1c..d9f4b4d494 100644 --- a/src/libponyc/pass/names.c +++ b/src/libponyc/pass/names.c @@ -162,6 +162,10 @@ static bool names_typealias(pass_opt_t* opt, ast_t** astp, ast_t* def, // Replace this with the alias. ast_replace(astp, r_alias); + + if(!expr) + ast_resetpass(*astp, PASS_NAME_RESOLUTION); + return true; } diff --git a/test/libponyc/annotations.cc b/test/libponyc/annotations.cc index c34d15b4c0..7823c4cd0e 100644 --- a/test/libponyc/annotations.cc +++ b/test/libponyc/annotations.cc @@ -23,7 +23,7 @@ TEST_F(AnnotationsTest, AnnotateTypes) } TEST_F(AnnotationsTest, AnnotateMethods) -{ +{ const char* src = "actor A\n" " new \\a, b\\ create() => None\n" @@ -109,7 +109,7 @@ TEST_F(AnnotationsTest, AnnotationsArePresent) TEST_COMPILE(src, "scope"); ast_t* ast = lookup_type("C"); - + ast = ast_annotation(ast); ASSERT_TRUE((ast != NULL) && (ast_id(ast) == TK_BACKSLASH)); @@ -139,12 +139,7 @@ TEST_F(AnnotationsTest, AnnotateSugar) " None\n" " else \\b\\ \n" " None\n" - " end\n" - - " fun test_object() =>\n" - " object \\a\\ \n" - " fun \\b\\ apply() => None\n" - " end"; + " end\n"; TEST_COMPILE(src, "scope"); @@ -165,14 +160,6 @@ TEST_F(AnnotationsTest, AnnotateSugar) ASSERT_TRUE(ast_has_annotation(ast, "a")); ASSERT_FALSE(ast_has_annotation(ast, "b")); ASSERT_TRUE(ast_has_annotation(ast_childidx(ast, 1), "b")); - - // Get the type of the object literal. - ast = lookup_type("$1$2"); - - ASSERT_TRUE(ast_has_annotation(ast, "a")); - ast = lookup_in(ast, "apply"); - - ASSERT_TRUE(ast_has_annotation(ast, "b")); } TEST_F(AnnotationsTest, AnnotateLambda) diff --git a/test/libponyc/sugar.cc b/test/libponyc/sugar.cc index 67a0979b67..0dce8627f0 100644 --- a/test/libponyc/sugar.cc +++ b/test/libponyc/sugar.cc @@ -1479,173 +1479,6 @@ TEST_F(SugarTest, AsDontCareMultiTuple) } -TEST_F(SugarTest, ObjectSimple) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun f() =>\n" - " object fun foo() => 4 end"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box f(): None =>\n" - " $T.create()\n" - " None\n" - - "primitive val $T\n" - " fun box foo(): None =>\n" - " 4\n" - " None\n" - " new val create(): $T val^ => true"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, ObjectWithField) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun f() =>\n" - " object let x: T = 3 fun foo() => 4 end"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box f(): None =>\n" - " $T.create(3)\n" - " None\n" - - "class ref $T\n" - " let x: T\n" - " fun box foo(): None =>\n" - " 4\n" - " None\n" - " new ref create($1: T): $T ref^ => x = consume $1"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, ObjectWithBehaviour) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun f() =>\n" - " object be foo() => 4 end"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box f(): None =>\n" - " $T.create()\n" - " None\n" - - "actor tag $T\n" - " be tag foo(): None =>\n" - " 4\n" - " new tag create(): $T tag^ => true"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, ObjectBox) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun f() =>\n" - " object box fun foo() => 4 end"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box f(): None =>\n" - " $T.create()\n" - " None\n" - - "primitive val $T\n" - " fun box foo(): None =>\n" - " 4\n" - " None\n" - " new val create(): $T val^ => true"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, ObjectRef) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun f() =>\n" - " object ref fun foo() => 4 end"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box f(): None =>\n" - " $T.create()\n" - " None\n" - - "class ref $T\n" - " fun box foo(): None =>\n" - " 4\n" - " None\n" - " new ref create(): $T ref^ => true"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, ObjectTagWithBehaviour) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun f() =>\n" - " object tag be foo() => 4 end"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box f(): None =>\n" - " $T.create()\n" - " None\n" - - "actor tag $T\n" - " be tag foo(): None =>\n" - " 4\n" - " new tag create(): $T tag^ => true"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, ObjectRefWithBehaviour) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun f() =>\n" - " object ref be foo() => 4 end"; - - TEST_ERROR(short_form); -} - - TEST_F(SugarTest, LambdaTypeSimple) { const char* short_form = From 84ca9eb02d2ff462f11023c1ff9b83c5d2f9db01 Mon Sep 17 00:00:00 2001 From: sylvanc Date: Thu, 9 Mar 2017 20:24:07 +0000 Subject: [PATCH 05/10] capture free variables --- src/libponyc/expr/lambda.c | 237 ++++++++++++++++++---------------- src/libponyc/expr/reference.c | 6 +- src/libponyc/pass/sugar.c | 2 +- src/libponyc/type/assemble.c | 18 ++- src/libponyc/type/assemble.h | 8 +- test/libponyc/sugar_expr.cc | 27 ---- 6 files changed, 148 insertions(+), 150 deletions(-) diff --git a/src/libponyc/expr/lambda.c b/src/libponyc/expr/lambda.c index cfe0a1c42b..1866b1e57f 100644 --- a/src/libponyc/expr/lambda.c +++ b/src/libponyc/expr/lambda.c @@ -7,6 +7,7 @@ #include "../pass/expr.h" #include "../pass/syntax.h" #include "../type/alias.h" +#include "../type/assemble.h" #include "../type/sanitise.h" #include "../pkg/package.h" #include "ponyassert.h" @@ -186,8 +187,8 @@ bool expr_lambda(pass_opt_t* opt, ast_t** astp) } -static bool capture_free_variable(pass_opt_t* opt, ast_t* ctx, ast_t* def, - ast_t* ast) +static bool capture_from_reference(pass_opt_t* opt, ast_t* ctx, ast_t* ast, + ast_t* captures, ast_t** last_capture) { const char* name = ast_name(ast_child(ast)); @@ -219,7 +220,14 @@ static bool capture_free_variable(pass_opt_t* opt, ast_t* ctx, ast_t* def, return false; } - // TODO: check if we've already captured it + // Check if we've already captured it + for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) + { + AST_GET_CHILDREN(p, c_name, c_type); + + if(name == ast_name(c_name)) + return true; + } // TODO: add as a field, add as a constructor parameter, init field from // parameter in constructor, add at the call site @@ -230,37 +238,19 @@ static bool capture_free_variable(pass_opt_t* opt, ast_t* ctx, ast_t* def, type = sanitise_type(type); - (void)def; - - printf("CAPTURE %s\n", name); - - ast_t* p_id = ast_from_string(ast, package_hygienic_id(&opt->check)); - - BUILD(field, def, + BUILD(field, ast, NODE(TK_FVAR, ID(name) TREE(type) - NONE)); - - BUILD(param, def, - NODE(TK_PARAM, - TREE(p_id) - TREE(type) - NONE)); - - BUILD(arg, ctx, NODE(TK_REFERENCE, ID(name))); - - BUILD(assign, def, - NODE(TK_ASSIGN, - NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) NODE(TK_REFERENCE, ID(name)))); + ast_list_append(captures, last_capture, field); return true; } -static bool capture_free_variables(pass_opt_t* opt, ast_t* ctx, ast_t* def, - ast_t* ast) +static bool capture_from_expr(pass_opt_t* opt, ast_t* ctx, ast_t* ast, + ast_t* capture, ast_t** last_capture) { // Skip preserved ASTs. if(ast_checkflag(ast, AST_FLAG_PRESERVE)) @@ -271,13 +261,13 @@ static bool capture_free_variables(pass_opt_t* opt, ast_t* ctx, ast_t* def, if(ast_id(ast) == TK_REFERENCE) { // Try to capture references. - if(!capture_free_variable(opt, ctx, def, ast)) + if(!capture_from_reference(opt, ctx, ast, capture, last_capture)) ok = false; } else { // Proceed down through all child ASTs. for(ast_t* p = ast_child(ast); p != NULL; p = ast_sibling(p)) { - if(!capture_free_variables(opt, ctx, def, p)) + if(!capture_from_expr(opt, ctx, p, capture, last_capture)) ok = false; } } @@ -286,6 +276,75 @@ static bool capture_free_variables(pass_opt_t* opt, ast_t* ctx, ast_t* def, } +static bool capture_from_type(pass_opt_t* opt, ast_t* ctx, ast_t** def, + ast_t* capture, ast_t** last_capture) +{ + // Turn any free variables into fields. + if(!ast_passes_type(def, opt, PASS_SCOPE)) + return false; + + bool ok = true; + ast_t* members = ast_childidx(*def, 4); + + for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) + { + switch(ast_id(p)) + { + case TK_FUN: + case TK_BE: + { + ast_t* body = ast_childidx(p, 6); + + if(!capture_from_expr(opt, ctx, body, capture, last_capture)) + ok = false; + break; + } + + default: {} + } + } + + // Reset the scope. + ast_clear(*def); + return ok; +} + + +static void add_field_to_object(pass_opt_t* opt, ast_t* field, + ast_t* class_members, ast_t* create_params, ast_t* create_body, + ast_t* call_args) +{ + AST_GET_CHILDREN(field, id, type, init); + ast_t* p_id = ast_from_string(id, package_hygienic_id(&opt->check)); + + // The param is: $0: type + BUILD(param, field, + NODE(TK_PARAM, + TREE(p_id) + TREE(type) + NONE)); + + // The arg is: $seq init + BUILD(arg, init, + NODE(TK_SEQ, + TREE(init))); + + // The body of create contains: id = consume $0 + BUILD(assign, init, + NODE(TK_ASSIGN, + NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) + NODE(TK_REFERENCE, TREE(id)))); + + // Remove the initialiser from the field + ast_replace(&init, ast_from(init, TK_NONE)); + + ast_add(class_members, field); + ast_append(create_params, param); + ast_append(create_body, assign); + ast_append(call_args, arg); +} + + bool expr_object(pass_opt_t* opt, ast_t** astp) { ast_t* ast = *astp; @@ -326,10 +385,11 @@ bool expr_object(pass_opt_t* opt, ast_t** astp) NONE ID("create") NONE + NODE(TK_PARAMS) NONE NONE - NONE - NODE(TK_SEQ) + NODE(TK_SEQ, + NODE(TK_TRUE)) NONE NONE)); @@ -347,7 +407,7 @@ bool expr_object(pass_opt_t* opt, ast_t** astp) // We will replace object..end with $0.create(...) BUILD(call, ast, NODE(TK_CALL, - NONE + NODE(TK_POSITIONALARGS) NONE NODE(TK_DOT, TREE(type_ref) @@ -370,41 +430,8 @@ bool expr_object(pass_opt_t* opt, ast_t** astp) case TK_FLET: case TK_EMBED: { - AST_GET_CHILDREN(member, id, type, init); - ast_t* p_id = ast_from_string(id, package_hygienic_id(&opt->check)); - - // The field is: var/let/embed id: type - BUILD(field, member, - NODE(ast_id(member), - TREE(id) - TREE(type) - NONE)); - - // The param is: $0: type - BUILD(param, member, - NODE(TK_PARAM, - TREE(p_id) - TREE(type) - NONE)); - - // The arg is: $seq init - BUILD(arg, init, - NODE(TK_SEQ, - TREE(init))); - - // The body of create contains: id = consume $0 - BUILD(assign, init, - NODE(TK_ASSIGN, - NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) - NODE(TK_REFERENCE, TREE(id)))); - - ast_setid(create_params, TK_PARAMS); - ast_setid(call_args, TK_POSITIONALARGS); - - ast_append(class_members, field); - ast_append(create_params, param); - ast_append(create_body, assign); - ast_append(call_args, arg); + add_field_to_object(opt, member, class_members, create_params, + create_body, call_args); has_fields = true; break; @@ -425,13 +452,30 @@ bool expr_object(pass_opt_t* opt, ast_t** astp) member = ast_sibling(member); } - if(!has_fields) + // Add the create function at the end. + ast_append(class_members, create); + + // Add new type to current module and bring it up to date with passes. + ast_t* module = ast_nearest(ast, TK_MODULE); + ast_append(module, def); + + // Turn any free variables into fields. + ast_t* captures = ast_from(ast, TK_MEMBERS); + ast_t* last_capture = NULL; + + if(!capture_from_type(opt, *astp, &def, captures, &last_capture)) + ok = false; + + for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { - // End the constructor with 'true', since it has no parameters. - BUILD(true_node, ast, NODE(TK_TRUE)); - ast_append(create_body, true_node); + add_field_to_object(opt, p, class_members, create_params, create_body, + call_args); + has_fields = true; } + ast_free_unattached(captures); + ast_resetpass(def, PASS_SUGAR); + // Handle capability and whether the anonymous type is a class, primitive or // actor. token_id cap_id = ast_id(cap); @@ -447,63 +491,30 @@ bool expr_object(pass_opt_t* opt, ast_t** astp) "actors and so must have tag capability"); ok = false; } + + cap_id = TK_TAG; } else if(!has_fields && (cap_id == TK_NONE || cap_id == TK_TAG || cap_id == TK_BOX || cap_id == TK_VAL)) { // Change the type from a class to a primitive. ast_setid(def, TK_PRIMITIVE); + cap_id = TK_VAL; } - else - { - // Type is a class, set the create capability as specified - ast_setid(ast_child(create), cap_id); - } - - // Add the create function at the end. - ast_append(class_members, create); - // Replace object..end with $0.create(...) - ast_t* module = ast_nearest(ast, TK_MODULE); - ast_replace(astp, call); - - // Add new type to current module and bring it up to date with passes. - ast_append(module, def); - - // Turn any free variables into fields. - if(!ast_passes_type(&def, opt, PASS_SCOPE)) - return false; - - members = ast_childidx(def, 4); - - for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) - { - switch(ast_id(p)) - { - case TK_FUN: - case TK_BE: - { - if(!capture_free_variables(opt, *astp, def, p)) - ok = false; - break; - } - - default: {} - } - } - - // Reset the scope and rewind the pass. - ast_clear(def); - ast_resetpass(def, PASS_SUGAR); - - // TODO: something about parameterised type aliases goes wrong - // nothing to do with the scope stuff above + // Reset constructor to pick up the correct defaults. + ast_setid(ast_child(create), cap_id); + ast_t* result = ast_childidx(create, 4); + ast_replace(&result, + type_for_class(opt, def, result, cap_id, TK_EPHEMERAL, false)); // Type check the anonymous type. if(!ast_passes_type(&def, opt, PASS_EXPR)) return false; - // Catch up passes + // Replace object..end with $0.create(...) + ast_replace(astp, call); + if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK) return false; diff --git a/src/libponyc/expr/reference.c b/src/libponyc/expr/reference.c index 9b5b3e9496..9b3fda5e24 100644 --- a/src/libponyc/expr/reference.c +++ b/src/libponyc/expr/reference.c @@ -172,7 +172,9 @@ bool expr_provides(pass_opt_t* opt, ast_t* ast) // happened. At this point, we need to check that the type is a structural // subtype of all traits and interfaces it declares as provided. AST_GET_CHILDREN(ast, id, typeparams, cap, provides); - ast_t* type = type_for_this(opt, ast, TK_REF, TK_NONE, true); + + ast_t* def = opt->check.frame->type; + ast_t* type = type_for_class(opt, def, ast, TK_REF, TK_NONE, true); errorframe_t err = NULL; if(!check_provides(opt, type, provides, &err)) @@ -1011,7 +1013,7 @@ bool expr_this(pass_opt_t* opt, ast_t* ast) make_arrow = true; } - ast_t* type = type_for_this(opt, ast, cap, TK_NONE, false); + ast_t* type = type_for_this(opt, ast, cap, TK_NONE); if(make_arrow) { diff --git a/src/libponyc/pass/sugar.c b/src/libponyc/pass/sugar.c index d65c1d17c5..ebf2a1b0e7 100644 --- a/src/libponyc/pass/sugar.c +++ b/src/libponyc/pass/sugar.c @@ -363,7 +363,7 @@ static ast_result_t sugar_new(pass_opt_t* opt, ast_t* ast) ast_setid(cap, tcap); } - ast_replace(&result, type_for_this(opt, ast, tcap, TK_EPHEMERAL, false)); + ast_replace(&result, type_for_this(opt, ast, tcap, TK_EPHEMERAL)); } sugar_docstring(ast); diff --git a/src/libponyc/type/assemble.c b/src/libponyc/type/assemble.c index 629e9e4cf7..819228bb0d 100644 --- a/src/libponyc/type/assemble.c +++ b/src/libponyc/type/assemble.c @@ -264,11 +264,10 @@ ast_t* type_isect(pass_opt_t* opt, ast_t* l_type, ast_t* r_type) return type_typeexpr(opt, TK_ISECTTYPE, l_type, r_type); } -ast_t* type_for_this(pass_opt_t* opt, ast_t* ast, token_id cap, - token_id ephemeral, bool defs) +ast_t* type_for_class(pass_opt_t* opt, ast_t* def, ast_t* ast, + token_id cap, token_id ephemeral, bool expr) { - typecheck_t* t = &opt->check; - AST_GET_CHILDREN(t->frame->type, id, typeparams); + AST_GET_CHILDREN(def, id, typeparams); BUILD(typeargs, ast, NODE(TK_NONE)); @@ -291,7 +290,7 @@ ast_t* type_for_this(pass_opt_t* opt, ast_t* ast, token_id cap, ast_t* typearg = type_sugar(ast, NULL, ast_name(typeparam_id)); ast_append(typeargs, typearg); - if(defs) + if(expr) { names_nominal(opt, ast, &typearg, false); @@ -303,12 +302,19 @@ ast_t* type_for_this(pass_opt_t* opt, ast_t* ast, token_id cap, } } - if(defs) + if(expr) names_nominal(opt, ast, &type, false); return type; } +ast_t* type_for_this(pass_opt_t* opt, ast_t* ast, token_id cap, + token_id ephemeral) +{ + typecheck_t* t = &opt->check; + return type_for_class(opt, t->frame->type, ast, cap, ephemeral, false); +} + ast_t* type_for_fun(ast_t* ast) { AST_GET_CHILDREN(ast, cap, name, typeparams, params, result); diff --git a/src/libponyc/type/assemble.h b/src/libponyc/type/assemble.h index 43a05559f1..9a6a0df3a9 100644 --- a/src/libponyc/type/assemble.h +++ b/src/libponyc/type/assemble.h @@ -47,11 +47,17 @@ ast_t* type_union(pass_opt_t* opt, ast_t* l_type, ast_t* r_type); */ ast_t* type_isect(pass_opt_t* opt, ast_t* l_type, ast_t* r_type); +/** + * Build a type to describe a class/actor. + */ +ast_t* type_for_class(pass_opt_t* opt, ast_t* def, ast_t* ast, + token_id cap, token_id ephemeral, bool expr); + /** * Build a type to describe the current class/actor. */ ast_t* type_for_this(pass_opt_t* opt, ast_t* ast, token_id cap, - token_id ephemeral, bool defs); + token_id ephemeral); /** * Build a type to describe a function signature. diff --git a/test/libponyc/sugar_expr.cc b/test/libponyc/sugar_expr.cc index 1dce9a3628..9b124bed7d 100644 --- a/test/libponyc/sugar_expr.cc +++ b/test/libponyc/sugar_expr.cc @@ -602,30 +602,3 @@ TEST_F(SugarExprTest, LocationDefaultArg) TEST_EQUIV(short_form, full_form); } - - -// Early sugar that may cause errors in type checking - -TEST_F(SugarExprTest, ObjectLiteralReferencingTypeParameters) -{ - const char* short_form = - "trait T\n" - - "class Foo[A: T]\n" - " fun f(x: A) =>\n" - " object let _x: A = consume x end"; - - const char* full_form = - "trait T\n" - - "class Foo[A: T]\n" - " fun f(x: A) =>\n" - " $T[A].create(consume x)\n" - - "class $T[A: T]\n" - " let _x: A\n" - " new create($1: A) =>\n" - " _x = consume $1"; - - TEST_EQUIV(short_form, full_form); -} From 8bc0cf4de4a8ed346745acedea8f37d8d2822a95 Mon Sep 17 00:00:00 2001 From: sylvanc Date: Thu, 9 Mar 2017 20:29:46 +0000 Subject: [PATCH 06/10] free variable capture test --- packages/builtin_test/_test.pony | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/builtin_test/_test.pony b/packages/builtin_test/_test.pony index 59699184bb..2cbc9770fa 100644 --- a/packages/builtin_test/_test.pony +++ b/packages/builtin_test/_test.pony @@ -64,6 +64,7 @@ actor Main is TestList test(_TestNextPow2) test(_TestNumberConversionSaturation) test(_TestMaybePointer) + test(_TestLambdaCapture) test(_TestValtrace) @@ -1544,3 +1545,15 @@ class iso _TestMaybePointer is UnitTest let from_b = b() h.assert_eq[U32](s.i, from_b.i) + + +class iso _TestLambdaCapture is UnitTest + """ + Test free variable capture in lambdas. + """ + fun name(): String => "builtin/LambdaCapture" + + fun apply(h: TestHelper) => + let x = "hi" + let f = {(y: String): String => x + y} + h.assert_eq[String]("hi there", f(" there")) From 37f0a8c08211d8da003523865d7d1360f8b71436 Mon Sep 17 00:00:00 2001 From: sylvanc Date: Thu, 9 Mar 2017 22:44:24 +0000 Subject: [PATCH 07/10] remove stray TODO comment --- src/libponyc/expr/lambda.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libponyc/expr/lambda.c b/src/libponyc/expr/lambda.c index 1866b1e57f..0fa06e735a 100644 --- a/src/libponyc/expr/lambda.c +++ b/src/libponyc/expr/lambda.c @@ -229,8 +229,6 @@ static bool capture_from_reference(pass_opt_t* opt, ast_t* ctx, ast_t* ast, return true; } - // TODO: add as a field, add as a constructor parameter, init field from - // parameter in constructor, add at the call site ast_t* type = alias(ast_type(refdef)); if(is_typecheck_error(type)) From eb0138da7f521411d01033a3924a85e733986f9a Mon Sep 17 00:00:00 2001 From: sylvanc Date: Thu, 9 Mar 2017 23:36:27 +0000 Subject: [PATCH 08/10] remove unnecessary explicit lambda captures in the stdlib --- packages/builtin_test/_test.pony | 10 ++++---- packages/collections/_test.pony | 6 ++--- packages/collections/persistent/_test.pony | 16 ++++++------- .../persistent/benchmarks/main.pony | 24 +++++++++---------- packages/itertools/_test.pony | 16 ++++++------- packages/itertools/iter.pony | 20 ++++++++-------- packages/itertools/itertools.pony | 6 ++--- packages/ponybench/_bench_async.pony | 2 +- packages/promises/_test.pony | 8 +++---- packages/regex/_test.pony | 4 ++-- 10 files changed, 56 insertions(+), 56 deletions(-) diff --git a/packages/builtin_test/_test.pony b/packages/builtin_test/_test.pony index 2cbc9770fa..bf32459139 100644 --- a/packages/builtin_test/_test.pony +++ b/packages/builtin_test/_test.pony @@ -1225,7 +1225,7 @@ class iso _TestArrayFind is UnitTest h.assert_eq[USize](1, a.find(1)) h.assert_eq[USize](5, a.find(1 where offset = 3)) h.assert_eq[USize](5, a.find(1 where nth = 1)) - h.assert_error({()(a)? => a.find(6) }) + h.assert_error({()? => a.find(6) }) h.assert_eq[USize](2, a.find(1 where predicate = {(l: ISize, r: ISize): Bool => l > r })) h.assert_eq[USize](0, a.find(0 where @@ -1240,14 +1240,14 @@ class iso _TestArrayFind is UnitTest h.assert_eq[USize](5, a.rfind(1)) h.assert_eq[USize](1, a.rfind(1 where offset = 3)) h.assert_eq[USize](1, a.rfind(1 where nth = 1)) - h.assert_error({()(a)? => a.rfind(6) }) + h.assert_error({()? => a.rfind(6) }) h.assert_eq[USize](4, a.rfind(1 where predicate = {(l: ISize, r: ISize): Bool => l > r })) h.assert_eq[USize](3, a.rfind(0 where predicate = {(l: ISize, r: ISize): Bool => (l % 3) == r })) h.assert_eq[USize](0, a.rfind(0 where predicate = {(l: ISize, r: ISize): Bool => (l % 3) == r }, nth = 1)) - h.assert_error({()(a)? => + h.assert_error({()? => a.rfind(0 where predicate = {(l: ISize, r: ISize): Bool => (l % 3) == r }, nth = 2) }) @@ -1255,7 +1255,7 @@ class iso _TestArrayFind is UnitTest var b = Array[_FindTestCls] let c = _FindTestCls b.push(c) - h.assert_error({()(b)? => b.find(_FindTestCls) }) + h.assert_error({()? => b.find(_FindTestCls) }) h.assert_eq[USize](0, b.find(c)) h.assert_eq[USize](0, b.find(_FindTestCls where predicate = {(l: _FindTestCls box, r: _FindTestCls box): Bool => l == r } @@ -1535,7 +1535,7 @@ class iso _TestMaybePointer is UnitTest let a = MaybePointer[_TestStruct].none() h.assert_true(a.is_none()) - h.assert_error({()(a)? => let from_a = a() }) + h.assert_error({()? => let from_a = a() }) let s = _TestStruct s.i = 7 diff --git a/packages/collections/_test.pony b/packages/collections/_test.pony index 706e7f4c1e..5b5d458286 100644 --- a/packages/collections/_test.pony +++ b/packages/collections/_test.pony @@ -210,15 +210,15 @@ class iso _TestRing is UnitTest a.>push(4).>push(5) - h.assert_error({()(a)? => a(0) }, "Read ring 0") - h.assert_error({()(a)? => a(1) }, "Read ring 1") + h.assert_error({()? => a(0) }, "Read ring 0") + h.assert_error({()? => a(1) }, "Read ring 1") h.assert_eq[U64](a(2), 2) h.assert_eq[U64](a(3), 3) h.assert_eq[U64](a(4), 4) h.assert_eq[U64](a(5), 5) - h.assert_error({()(a)? => a(6) }, "Read ring 6") + h.assert_error({()? => a(6) }, "Read ring 6") class iso _TestListsMap is UnitTest fun name(): String => "collections/Lists/map()" diff --git a/packages/collections/persistent/_test.pony b/packages/collections/persistent/_test.pony index 90d9907cf5..eeeb6bd882 100644 --- a/packages/collections/persistent/_test.pony +++ b/packages/collections/persistent/_test.pony @@ -75,11 +75,11 @@ class iso _TestListApply is UnitTest h.assert_eq[U32](l1(0), 1) h.assert_eq[U32](l1(1), 2) h.assert_eq[U32](l1(2), 3) - h.assert_error({()(l1)? => l1(3) }) - h.assert_error({()(l1)? => l1(4) }) + h.assert_error({()? => l1(3) }) + h.assert_error({()? => l1(4) }) let l2 = Lists[U32].empty() - h.assert_error({()(l2)? => l2(0) }) + h.assert_error({()? => l2(0) }) class iso _TestListValues is UnitTest fun name(): String => "collections/persistent/List/values()" @@ -253,7 +253,7 @@ class iso _TestMap is UnitTest fun apply(h: TestHelper) => let m1: Map[String,U32] = Maps.empty[String,U32]() - h.assert_error({()(m1)? => m1("a") }) + h.assert_error({()? => m1("a") }) let s1 = m1.size() h.assert_eq[USize](s1, 0) @@ -281,17 +281,17 @@ class iso _TestMap is UnitTest h.assert_eq[U32](m7("b"), 3) h.assert_eq[U32](m7("a"), 10) let m8 = m7.remove("a") - h.assert_error({()(m8 = m8)? => m8("a") }) + h.assert_error({()? => m8("a") }) h.assert_eq[U32](m8("b"), 3) h.assert_eq[U32](m8("d"), 4) h.assert_eq[U32](m8("e"), 5) let m9 = m7.remove("e") - h.assert_error({()(m9 = m9)? => m9("e") }) + h.assert_error({()? => m9("e") }) h.assert_eq[U32](m9("b"), 3) h.assert_eq[U32](m9("d"), 4) let m10 = m9.remove("b").remove("d") - h.assert_error({()(m10 = m10)? => m10("b") }) - h.assert_error({()(m10 = m10)? => m10("d") }) + h.assert_error({()? => m10("b") }) + h.assert_error({()? => m10("d") }) else h.complete(false) end diff --git a/packages/collections/persistent/benchmarks/main.pony b/packages/collections/persistent/benchmarks/main.pony index fd8b6a2375..3a6439c650 100644 --- a/packages/collections/persistent/benchmarks/main.pony +++ b/packages/collections/persistent/benchmarks/main.pony @@ -4,51 +4,51 @@ use "ponybench" actor Main new create(env: Env) => let bench = PonyBench(env) - + var map = Maps.empty[U64, U64]() try map = map.update(0, 0) end - + bench[Map[U64, U64]]( "insert level 0", - {()(map): Map[U64, U64] ? => map.update(1, 1)} val) + {(): Map[U64, U64] ? => map.update(1, 1)} val) bench[U64]( "get level 0", - {()(map): U64 ? => map(0)} val) + {(): U64 ? => map(0)} val) bench[Map[U64, U64]]( "update level 0", - {()(map): Map[U64, U64] ? => map.update(0, 1)} val) + {(): Map[U64, U64] ? => map.update(0, 1)} val) bench[Map[U64, U64]]( "delete level 0", - {()(map): Map[U64, U64] ? => map.remove(0)} val) + {(): Map[U64, U64] ? => map.remove(0)} val) bench[Map[U64, U64]]( "create sub-node", - {()(map): Map[U64, U64] ? => map.update(32, 32)} val) + {(): Map[U64, U64] ? => map.update(32, 32)} val) // expand index 0 into 2 sub-nodes try map = map.update(32, 32) end bench[Map[U64, U64]]( "remove sub-node", - {()(map): Map[U64, U64] ? => map.remove(32)} val) + {(): Map[U64, U64] ? => map.remove(32)} val) bench[Map[U64, U64]]( "insert level 1", - {()(map): Map[U64, U64] ? => map.update(1, 1)} val) + {(): Map[U64, U64] ? => map.update(1, 1)} val) bench[U64]( "get level 1", - {()(map): U64 ? => map(0)} val) + {(): U64 ? => map(0)} val) bench[Map[U64, U64]]( "update level 1", - {()(map): Map[U64, U64] ? => map.update(0, 1)} val) + {(): Map[U64, U64] ? => map.update(0, 1)} val) try map = map.update(1, 1) end bench[Map[U64, U64]]( "delete level 1", - {()(map): Map[U64, U64] ? => map.remove(1)} val) + {(): Map[U64, U64] ? => map.remove(1)} val) diff --git a/packages/itertools/_test.pony b/packages/itertools/_test.pony index e813380af3..2949fc2723 100644 --- a/packages/itertools/_test.pony +++ b/packages/itertools/_test.pony @@ -401,7 +401,7 @@ class iso _TestIterLast is UnitTest fun apply(h: TestHelper) ? => let input1 = Array[I64] - h.assert_error({()(input1) ? => Iter[I64](input1.values()).last() }) + h.assert_error({()? => Iter[I64](input1.values()).last() }) let input2 =[as I64: 1] h.assert_eq[I64]( 1, @@ -423,7 +423,7 @@ class iso _TestIterMap is UnitTest let expected = ["ab", "bb", "cb"] let actual = Array[String] - let fn = { (x: String): String => x + "b" } + let fn = {(x: String): String => x + "b" } for x in Iter[String](input.values()).map[String](fn) do actual.push(x) end @@ -444,7 +444,7 @@ class iso _TestIterNth is UnitTest h.assert_eq[USize]( 3, Iter[USize](input.values()).nth(3)) - h.assert_error({()(input) ? => Iter[USize](input.values()).nth(4) }) + h.assert_error({()? => Iter[USize](input.values()).nth(4) }) class iso _TestIterRun is UnitTest fun name(): String => "itertools/Iter.run" @@ -454,26 +454,26 @@ class iso _TestIterRun is UnitTest h.expect_action("2") h.expect_action("3") h.expect_action("error") - + let xs = [as I64: 1, 2, 3] h.long_test(100_000_000) - + Iter[I64](xs.values()) - .map[None]({(x: I64)(h) => h.complete_action(x.string()) }) + .map[None]({(x: I64) => h.complete_action(x.string()) }) .run() Iter[I64](object ref is Iterator[I64] fun ref has_next(): Bool => true fun ref next(): I64 ? => error - end).run({()(h) => h.complete_action("error") }) + end).run({() => h.complete_action("error") }) class iso _TestIterSkip is UnitTest fun name(): String => "itertools/Iter.skip" fun apply(h: TestHelper) ? => let input = [as I64: 1] - h.assert_error({()(input) ? => + h.assert_error({()? => Iter[I64](input.values()).skip(1).next() }) input.push(2) diff --git a/packages/itertools/iter.pony b/packages/itertools/iter.pony index f0b814fcd1..14236ad7fb 100644 --- a/packages/itertools/iter.pony +++ b/packages/itertools/iter.pony @@ -59,7 +59,7 @@ class Iter[A] is Iterator[A] fun ref any(f: {(A!): Bool ?} box): Bool => """ - Return true if at least one value of the iterator matches the predicate + Return true if at least one value of the iterator matches the predicate `f`. This method short-circuits at the first value where the predicate returns true, otherwise false is returned. @@ -87,7 +87,7 @@ class Iter[A] is Iterator[A] fun ref collect[B: Seq[A!] ref = Array[A!]](coll: B): B^ => """ Push each value from the iterator into the collection `coll`. - + ## Example ```pony @@ -190,7 +190,7 @@ class Iter[A] is Iterator[A] fun ref filter(f: {(A!): Bool ?} box): Iter[A]^ => """ Return an iterator that only returns items that match the predicate `f`. - + ## Example ```pony @@ -249,7 +249,7 @@ class Iter[A] is Iterator[A] fun ref find(f: {(A!): Bool ?} box, n: USize = 1): A ? => """ Return the nth value in the iterator that satisfies the predicate `f`. - + ## Examples ```pony @@ -279,7 +279,7 @@ class Iter[A] is Iterator[A] fun ref fold[B](f: {(B, A!): B^ ?} box, acc: B): B^ ? => """ Apply a function to every element, producing an accumulated value. - + ## Example ```pony @@ -315,7 +315,7 @@ class Iter[A] is Iterator[A] """ Return an iterator where each item's value is the application of the given function to the value in the original iterator. - + ## Example ```pony @@ -372,7 +372,7 @@ class Iter[A] is Iterator[A] ```pony Iter[I64]([as I64: 1, 2, 3].values()) - .map[None]({(x: I64)(env) => env.out.print(x.string()) }) + .map[None]({(x: I64) => env.out.print(x.string()) }) .run() ``` ``` @@ -382,8 +382,8 @@ class Iter[A] is Iterator[A] ``` """ if not _iter.has_next() then return end - try - _iter.next() + try + _iter.next() run(on_error) else on_error() @@ -413,7 +413,7 @@ class Iter[A] is Iterator[A] fun ref skip_while(f: {(A!): Bool ?} box): Iter[A]^ => """ Skip values of the iterator while the predicate `f` returns true. - + ## Example ```pony diff --git a/packages/itertools/itertools.pony b/packages/itertools/itertools.pony index 8d56c4931f..55293fe400 100644 --- a/packages/itertools/itertools.pony +++ b/packages/itertools/itertools.pony @@ -8,7 +8,7 @@ Elixir's Enum and Stream. ## Iter The Iter class wraps iterators so that additional methods may be applied to it. -Some methods, such as fold and collect, run through the underlying iterator in +Some methods, such as fold and collect, run through the underlying iterator in order to return a result. Others, such as map and filter, are lazy. This means that they return another Iter so that the resulting values are computed one by one as needed. Lazy methods return Iter types. @@ -21,7 +21,7 @@ any odd numbers, and prints the rest. let xs = Iter[I64]([as I64: 1, 2, 3, 4, 5].values()) .map[I64]({(x: I64): I64 => x + 1 }) .filter({(x: I64): Bool => (x % 2) == 0 }) - .map[None]({(x: I64)(env) => env.out.print(x.string()) }) + .map[None]({(x: I64) => env.out.print(x.string()) }) ``` This will result in an iterator that prints the numbers 2, 4, and 6. However, @@ -43,7 +43,7 @@ for a loop. So the final code would be as follows: Iter[I64]([as I64: 1, 2, 3, 4, 5].values()) .map[I64]({(x: I64): I64 => x + 1 }) .filter({(x: I64): Bool => (x % 2) == 0 }) - .map[None]({(x: I64)(env) => env.out.print(x.string()) }) + .map[None]({(x: I64) => env.out.print(x.string()) }) .run() ``` diff --git a/packages/ponybench/_bench_async.pony b/packages/ponybench/_bench_async.pony index 1772ce0878..46f40ad64f 100644 --- a/packages/ponybench/_bench_async.pony +++ b/packages/ponybench/_bench_async.pony @@ -46,7 +46,7 @@ actor _BenchAsync[A: Any #share] is _Benchmark else try let n: _BenchAsync[A] tag = this - _f().next[None]({(a: A)(n, i) => n._run(i+1)} iso) + _f().next[None]({(a: A) => n._run(i + 1)} iso) else _notify._failure(_name, false) end diff --git a/packages/promises/_test.pony b/packages/promises/_test.pony index 9cf3b45158..820ffa2990 100644 --- a/packages/promises/_test.pony +++ b/packages/promises/_test.pony @@ -18,16 +18,16 @@ class iso _TestPromise is UnitTest fun _test_fulfilled(h: TestHelper) => h.expect_action("fulfilled") let p = Promise[String] - p.next[None]({(s: String)(h) => h.complete_action(s) } iso) + p.next[None]({(s: String) => h.complete_action(s) } iso) p("fulfilled") fun _test_rejected(h: TestHelper) => h.expect_action("rejected") let p = Promise[String] p.next[String]( - {(s: String)(h): String ? => error } iso, - {()(h): String => "rejected" } iso + {(s: String): String ? => error } iso, + {(): String => "rejected" } iso ).next[None]( - {(s: String)(h) => h.complete_action(s) } iso + {(s: String) => h.complete_action(s) } iso ) p.reject() diff --git a/packages/regex/_test.pony b/packages/regex/_test.pony index bc7e627077..3d202a67ba 100644 --- a/packages/regex/_test.pony +++ b/packages/regex/_test.pony @@ -41,12 +41,12 @@ class iso _TestGroups is UnitTest let m2 = r("123.") h.assert_eq[String](m2(0), "123.") h.assert_eq[String](m2(1), "123") - h.assert_error({()(m2 = m2)? => m2(2) }) + h.assert_error({()? => m2(2) }) h.assert_array_eq[String](m2.groups(), ["123", ""]) let m3 = r(".456") h.assert_eq[String](m3(0), ".456") - h.assert_error({()(m3 = m3)? => m3(1) }) + h.assert_error({()? => m3(1) }) h.assert_eq[String](m3(2), "456") h.assert_array_eq[String](m3.groups(), ["", "456"]) From 71b848e9df8140c60ff13a00d2fd2d345c0e7cf7 Mon Sep 17 00:00:00 2001 From: sylvanc Date: Fri, 10 Mar 2017 21:15:51 +0000 Subject: [PATCH 09/10] fix macro typo --- src/libponyc/ast/astbuild.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libponyc/ast/astbuild.h b/src/libponyc/ast/astbuild.h index 444cbfc715..311b3ccb2f 100644 --- a/src/libponyc/ast/astbuild.h +++ b/src/libponyc/ast/astbuild.h @@ -69,7 +69,7 @@ #define TREE_CLEAR_PASS(tree) \ { \ if(ast_parent(tree) != NULL) tree = ast_dup(tree); \ - ast_resetpass(tree. 0); \ + ast_resetpass(tree, 0); \ TREE(tree); \ } From 8359f0a554143cb86bc5f0aab05cc31850f098f8 Mon Sep 17 00:00:00 2001 From: sylvanc Date: Fri, 10 Mar 2017 22:35:59 +0000 Subject: [PATCH 10/10] lambda and object literal tests through the expr pass --- test/libponyc/lambda.cc | 164 ++++++++++++++++++++++++++++++++++++ test/libponyc/parse_expr.cc | 80 ------------------ 2 files changed, 164 insertions(+), 80 deletions(-) create mode 100644 test/libponyc/lambda.cc diff --git a/test/libponyc/lambda.cc b/test/libponyc/lambda.cc new file mode 100644 index 0000000000..71a345190f --- /dev/null +++ b/test/libponyc/lambda.cc @@ -0,0 +1,164 @@ +#include +#include "util.h" + + +// Parsing tests regarding expressions + +#define TEST_ERROR(src) DO(test_error(src, "expr")) +#define TEST_COMPILE(src) DO(test_compile(src, "expr")) + +#define TEST_ERRORS_1(src, err1) \ + { const char* errs[] = {err1, NULL}; \ + DO(test_expected_errors(src, "expr", errs)); } + + +class LambdaTest : public PassTest +{}; + + +// Lambda tests + +TEST_F(LambdaTest, Lambda) +{ + const char* src = + "class Foo\n" + " fun f() =>\n" + " {() => None}"; + + TEST_COMPILE(src); +} + + +TEST_F(LambdaTest, LambdaCap) +{ + const char* src = + "class Foo\n" + " fun f() =>\n" + " {iso() => None}"; + + TEST_COMPILE(src); +} + + +TEST_F(LambdaTest, LambdaCaptureVariable) +{ + const char* src = + "class Foo\n" + " fun f() =>\n" + " let x = \"hi\"\n" + " {()(x): String => x}"; + + TEST_COMPILE(src); +} + + +TEST_F(LambdaTest, LambdaCaptureExpression) +{ + const char* src = + "class Foo\n" + " fun f() =>\n" + " let y = \"hi\"\n" + " {()(x = y): String => x}"; + + TEST_COMPILE(src); +} + + +TEST_F(LambdaTest, LambdaCaptureExpressionAndType) +{ + const char* src = + "class Foo\n" + " fun f() =>\n" + " let y = \"hi\"\n" + " {()(x: String = y): String => x}"; + + TEST_COMPILE(src); +} + + +TEST_F(LambdaTest, LambdaCaptureTypeWithoutExpressionFail) +{ + const char* src = + "class Foo\n" + " fun f() =>\n" + " let x = \"hi\"\n" + " {()(x: String): String => x}"; + + TEST_ERROR(src); +} + + +TEST_F(LambdaTest, LambdaOldSyntax) +{ + const char* src = + "actor Main\n" + " new create(env: Env) =>\n" + " lambda() => None end"; + + TEST_ERRORS_1(src, + "lambda ... end is no longer supported syntax; use {...} for lambdas"); +} + + +TEST_F(LambdaTest, LambdaCaptureFree) +{ + const char* src = + "class Foo\n" + " fun f() =>\n" + " let x = \"hi\"\n" + " {(): String => x}"; + + TEST_COMPILE(src); +} + + +TEST_F(LambdaTest, LambdaCaptureRefInIso) +{ + const char* src = + "class Foo\n" + " fun f() =>\n" + " let x: String ref = String\n" + " {(): String ref => x} iso"; + + TEST_ERROR(src); +} + + +TEST_F(LambdaTest, LambdaCaptureRefInVal) +{ + const char* src = + "class Foo\n" + " fun f() =>\n" + " let x: String ref = String\n" + " {(): String ref => x} val"; + + TEST_ERROR(src); +} + + +TEST_F(LambdaTest, ObjectCaptureFree) +{ + const char* src = + "class Foo\n" + " fun f() =>\n" + " let x = \"hi\"\n" + " object\n" + " fun apply(): String => x\n" + " end"; + + TEST_COMPILE(src); +} + + +TEST_F(LambdaTest, ObjectCaptureRefInActor) +{ + const char* src = + "class Foo\n" + " fun f() =>\n" + " let x: String ref = String\n" + " object\n" + " be apply(): String ref => x\n" + " end"; + + TEST_ERROR(src); +} diff --git a/test/libponyc/parse_expr.cc b/test/libponyc/parse_expr.cc index fde0fcc730..a8f7173411 100644 --- a/test/libponyc/parse_expr.cc +++ b/test/libponyc/parse_expr.cc @@ -16,86 +16,6 @@ class ParseExprTest : public PassTest {}; -// Lambda tests - -TEST_F(ParseExprTest, Lambda) -{ - const char* src = - "class Foo\n" - " fun f() =>\n" - " {() => None }"; - - TEST_COMPILE(src); -} - - -TEST_F(ParseExprTest, LambdaCap) -{ - const char* src = - "class Foo\n" - " fun f() =>\n" - " {iso() => None }"; - - TEST_COMPILE(src); -} - - -TEST_F(ParseExprTest, LambdaCaptureVariable) -{ - const char* src = - "class Foo\n" - " fun f() =>\n" - " {()(x) => None }"; - - TEST_COMPILE(src); -} - - -TEST_F(ParseExprTest, LambdaCaptureExpression) -{ - const char* src = - "class Foo\n" - " fun f() =>\n" - " {()(x = y) => None }"; - - TEST_COMPILE(src); -} - - -TEST_F(ParseExprTest, LambdaCaptureExpressionAndType) -{ - const char* src = - "class Foo\n" - " fun f() =>\n" - " {()(x: T = y) => None }"; - - TEST_COMPILE(src); -} - - -TEST_F(ParseExprTest, LambdaCaptureTypeWithoutExpressionFail) -{ - const char* src = - "class Foo\n" - " fun f() =>\n" - " {()(x: T) => None }"; - - TEST_ERROR(src); -} - - -TEST_F(ParseExprTest, LambdaOldSyntax) -{ - const char* src = - "actor Main\n" - " new create(env: Env) =>\n" - " lambda() => None end"; - - TEST_ERRORS_1(src, - "lambda ... end is no longer supported syntax; use {...} for lambdas"); -} - - // Compile error TEST_F(ParseExprTest, CompileErrorAllowedAsIfdefClause)