diff --git a/pony.g b/pony.g index 8dbc8327e18..a98cb6fbc63 100644 --- a/pony.g +++ b/pony.g @@ -208,8 +208,8 @@ nextatom | LPAREN_NEW rawseq tuple? ')' | LSQUARE_NEW ('as' type ':')? rawseq? ']' | 'object' ('\\' ID (',' ID)* '\\')? cap? ('is' type)? members 'end' - | '{' ('\\' ID (',' ID)* '\\')? cap? ID? typeparams? ('(' | LPAREN_NEW) params? ')' lambdacaptures? (':' type)? '?'? '=>' rawseq '}' cap? - | '@{' ('\\' ID (',' ID)* '\\')? cap? ID? typeparams? ('(' | LPAREN_NEW) params? ')' lambdacaptures? (':' type)? '?'? '=>' rawseq '}' cap? + | '{' ('\\' ID (',' ID)* '\\')? cap? ID? typeparams? ('(' | LPAREN_NEW) lambdaparams? ')' lambdacaptures? (':' type)? '?'? '=>' rawseq '}' cap? + | '@{' ('\\' ID (',' ID)* '\\')? cap? ID? typeparams? ('(' | LPAREN_NEW) lambdaparams? ')' lambdacaptures? (':' type)? '?'? '=>' rawseq '}' cap? | '@' (ID | STRING) typeargs? ('(' | LPAREN_NEW) positional? named? ')' '?'? | '__loc' ; @@ -221,8 +221,8 @@ atom | ('(' | LPAREN_NEW) rawseq tuple? ')' | ('[' | LSQUARE_NEW) ('as' type ':')? rawseq? ']' | 'object' ('\\' ID (',' ID)* '\\')? cap? ('is' type)? members 'end' - | '{' ('\\' ID (',' ID)* '\\')? cap? ID? typeparams? ('(' | LPAREN_NEW) params? ')' lambdacaptures? (':' type)? '?'? '=>' rawseq '}' cap? - | '@{' ('\\' ID (',' ID)* '\\')? cap? ID? typeparams? ('(' | LPAREN_NEW) params? ')' lambdacaptures? (':' type)? '?'? '=>' rawseq '}' cap? + | '{' ('\\' ID (',' ID)* '\\')? cap? ID? typeparams? ('(' | LPAREN_NEW) lambdaparams? ')' lambdacaptures? (':' type)? '?'? '=>' rawseq '}' cap? + | '@{' ('\\' ID (',' ID)* '\\')? cap? ID? typeparams? ('(' | LPAREN_NEW) lambdaparams? ')' lambdacaptures? (':' type)? '?'? '=>' rawseq '}' cap? | '@' (ID | STRING) typeargs? ('(' | LPAREN_NEW) positional? named? ')' '?'? | '__loc' ; @@ -239,6 +239,14 @@ lambdacapture : ID (':' type)? ('=' infix)? ; +lambdaparams + : lambdaparam (',' lambdaparam)* + ; + +lambdaparam + : parampattern (':' type)? ('=' infix)? + ; + positional : rawseq (',' rawseq)* ; @@ -340,7 +348,7 @@ literal ; param - : parampattern (':' type)? ('=' infix)? + : ID (':' type)? ('=' infix)? ; antlr_0 diff --git a/src/libponyc/ast/parser.c b/src/libponyc/ast/parser.c index 82404cc44f3..9a0d9331542 100644 --- a/src/libponyc/ast/parser.c +++ b/src/libponyc/ast/parser.c @@ -73,14 +73,11 @@ DEF(defaultarg); RULE("default value", infix); DONE(); -// postfix [COLON type] [ASSIGN defaultarg] +// ID [COLON type] [ASSIGN defaultarg] DEF(param); AST_NODE(TK_PARAM); - RULE("name", parampattern); - IF(TK_COLON, - RULE("parameter type", type); - UNWRAP(0, TK_REFERENCE); - ); + TOKEN("parameter name", TK_ID); + IF(TK_COLON, RULE("parameter type", type)); IF(TK_ASSIGN, RULE("default value", defaultarg)); DONE(); @@ -344,6 +341,24 @@ DEF(object); SET_CHILD_FLAG(2, AST_FLAG_PRESERVE); // Members DONE(); +// parampattern [COLON type] [ASSIGN defaultarg] +DEF(lambdaparam); + AST_NODE(TK_PARAM); + RULE("name", parampattern); + IF(TK_COLON, + RULE("parameter type", type); + UNWRAP(0, TK_REFERENCE); + ); + IF(TK_ASSIGN, RULE("default value", defaultarg)); + DONE(); + +// lambdaparam {COMMA lambdaparam} +DEF(lambdaparams); + AST_NODE(TK_PARAMS); + RULE("parameter", lambdaparam); + WHILE(TK_COMMA, RULE("parameter", lambdaparam)); + DONE(); + // ID [COLON type] [ASSIGN infix] DEF(lambdacapture); AST_NODE(TK_LAMBDACAPTURE); @@ -362,8 +377,9 @@ DEF(lambdacaptures); SKIP(NULL, TK_RPAREN); DONE(); -// LBRACE [annotations] [CAP] [ID] [typeparams] (LPAREN | LPAREN_NEW) [params] -// RPAREN [lambdacaptures] [COLON type] [QUESTION] ARROW rawseq RBRACE [CAP] +// LBRACE [annotations] [CAP] [ID] [typeparams] (LPAREN | LPAREN_NEW) +// [lambdaparams] RPAREN [lambdacaptures] [COLON type] [QUESTION] ARROW rawseq +// RBRACE [CAP] DEF(lambda); PRINT_INLINE(); AST_NODE(TK_LAMBDA); @@ -373,7 +389,7 @@ DEF(lambda); OPT TOKEN("function name", TK_ID); OPT RULE("type parameters", typeparams); SKIP(NULL, TK_LPAREN, TK_LPAREN_NEW); - OPT RULE("parameters", params); + OPT RULE("parameters", lambdaparams); SKIP(NULL, TK_RPAREN); OPT RULE("captures", lambdacaptures); IF(TK_COLON, RULE("return type", type)); @@ -389,8 +405,8 @@ DEF(lambda); DONE(); // AT_LBRACE [annotations] [CAP] [ID] [typeparams] (LPAREN | LPAREN_NEW) -// [params] RPAREN [lambdacaptures] [COLON type] [QUESTION] ARROW rawseq RBRACE -// [CAP] +// [lambdaparams] RPAREN [lambdacaptures] [COLON type] [QUESTION] ARROW rawseq +// RBRACE [CAP] DEF(barelambda); PRINT_INLINE(); AST_NODE(TK_BARELAMBDA); @@ -400,7 +416,7 @@ DEF(barelambda); OPT TOKEN("function name", TK_ID); OPT RULE("type parameters", typeparams); SKIP(NULL, TK_LPAREN, TK_LPAREN_NEW); - OPT RULE("parameters", params); + OPT RULE("parameters", lambdaparams); SKIP(NULL, TK_RPAREN); OPT RULE("captures", lambdacaptures); IF(TK_COLON, RULE("return type", type)); diff --git a/src/libponyc/pass/casemethod.c b/src/libponyc/pass/casemethod.c deleted file mode 100644 index f8c0d5c9fd6..00000000000 --- a/src/libponyc/pass/casemethod.c +++ /dev/null @@ -1,938 +0,0 @@ -#include "casemethod.h" -#include "sugar.h" -#include "../ast/astbuild.h" -#include "../ast/id.h" -#include "../ast/printbuf.h" -#include "../pkg/package.h" -#include "../../libponyrt/mem/pool.h" -#include "ponyassert.h" -#include - -/* The following sugar handles case methods. - -Case methods are the methods appearing in the source code appearing to be -overloaded. A match method is the set of case methods for a given method name -within a single entity, treated as a single overall method. - -We remove case methods from an entity and replace them with notionally a single -function. In practice we actually create 2 functions, to allow us to make -parameter and capture names work sensibly. These 2 functions are: - -1. The wrapper function. -Uses the parameter names specified in the case methods, which allows calling -with named arguments to work as expected. -All this function does is call the worker function. -For case behaviours this is a behaviour, for case functions it is a function. - -2. The worker method. -This uses hygenic parameter names, which leaves the names used by the case -methods free to be used for captures. -Contains the match expression that selects which case body to use. - -Example source code: - -class C - fun foo(0): U64 => 0 - fun foo(x: U64): U64 if x < 3 => 1 - fun foo(x: U64): U64 => 2 - -would be translated to: - -class C - fun foo(x: U64): U64 => // wrapper - $1(x) - - fun $1($2: U64): U64 => // worker - match $2 - | 0 => 0 - | let x: U64 if x < 3 => 1 - | let x: U64 => 2 - end - -*/ - - -// Make a skeleton wrapper method based on the given case method. -// Returns: created method, NULL on error. -static ast_t* make_match_wrapper(ast_t* case_method, pass_opt_t* opt) -{ - pony_assert(case_method != NULL); - - if(ast_id(case_method) == TK_FUN) - fun_defaults(case_method); - - AST_GET_CHILDREN(case_method, cap, id, t_params, params, ret_type, question, - body, docstring); - - if(ast_child(params) == NULL) - { - ast_error(opt->check.errors, params, - "case method must have at least one parameter"); - return NULL; - } - - ast_t* new_params = ast_from(params, TK_PARAMS); - ast_t* param_list_end = NULL; - - for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p)) - { - // Set all parameter info to none now and fill it in later. - BUILD(new_param, p, NODE(TK_PARAM, NONE NONE NONE)); - ast_list_append(new_params, ¶m_list_end, new_param); - } - - ast_t* new_t_params = ast_from(params, TK_NONE); - ast_t* t_param_list_end = NULL; - - for(ast_t* p = ast_child(t_params); p != NULL; p = ast_sibling(p)) - { - // Set all type parameter info to none now and fill it in later. - BUILD(new_t_param, p, NODE(TK_TYPEPARAM, NONE NONE NONE)); - ast_list_append(new_t_params, &t_param_list_end, new_t_param); - ast_setid(new_t_params, TK_TYPEPARAMS); - } - - // provide an empty docstring to build upon - token_t *empty = token_new(TK_STRING); - token_set_string(empty, "", 0); - ast_t* wrapper_docstring = ast_token(empty); - - if(ast_id(case_method) == TK_FUN) - { - // Function case. - - // TODO: For now always need a `None |` in the return type to allow for - // match else clause. We won't need that once exhaustive matching is done. - BUILD(wrapper, case_method, - NODE(ast_id(case_method), AST_SCOPE - TREE(cap) - TREE(id) - TREE(new_t_params) - TREE(new_params) - NODE(TK_NOMINAL, NONE ID("None") NONE NONE NONE) // Return value. - NONE // Error. - NODE(TK_SEQ) // Body. - TREE(wrapper_docstring) // Doc string. - NONE)); // Guard. - - return wrapper; - } - else - { - // Behaviour case. - BUILD(wrapper, case_method, - NODE(ast_id(case_method), AST_SCOPE - NONE // Capability. - TREE(id) - TREE(new_t_params) - TREE(new_params) - NONE // Return value. - NONE // Error. - NODE(TK_SEQ) // Body. - TREE(wrapper_docstring) // Doc string. - NONE)); // Guard. - - return wrapper; - } -} - - -// Handle the given case method parameter, which does have a type specified. -// Combine and check the case parameter against the existing match parameter -// and generate the pattern element. -// Returns: true on success, false on error. -static bool param_with_type(ast_t* case_param, ast_t* match_param, - ast_t* pattern, pass_opt_t* opt) -{ - pony_assert(case_param != NULL); - pony_assert(match_param != NULL); - pony_assert(pattern != NULL); - - AST_GET_CHILDREN(case_param, case_id, case_type, case_def_arg); - pony_assert(ast_id(case_type) != TK_NONE); - - if(ast_id(case_id) != TK_ID) - { - ast_error(opt->check.errors, case_id, "expected parameter name for " - "case method. If this is meant to be a value to match on, do not " - "specify parameter type"); - return false; - } - - AST_GET_CHILDREN(match_param, match_id, match_type, match_def_arg); - pony_assert(ast_id(match_id) == TK_ID || ast_id(match_id) == TK_NONE); - - if(ast_id(match_id) == TK_NONE) - { - // This is the first case method to give this parameter, just use it as - // given by this case. - ast_replace(&match_id, case_id); - ast_replace(&match_type, case_type); - ast_replace(&match_def_arg, case_def_arg); - } - else - { - // Combine new case param with existing match param. - if(ast_name(case_id) != ast_name(match_id)) - { - ast_error(opt->check.errors, case_id, - "parameter name differs between case methods"); - ast_error_continue(opt->check.errors, match_id, "clashing name here"); - return false; - } - - if(ast_id(case_def_arg) != TK_NONE) - { - // This case provides a default argument. - if(ast_id(match_def_arg) != TK_NONE) - { - ast_error(opt->check.errors, case_def_arg, - "multiple defaults provided for case method parameter"); - ast_error_continue(opt->check.errors, match_def_arg, - "another default defined here"); - return false; - } - - // Move default arg to match parameter. - ast_replace(&match_def_arg, case_def_arg); - } - - // Union param types. - REPLACE(&match_type, - NODE(TK_UNIONTYPE, - TREE(match_type) - TREE(case_type))); - } - - // Add parameter to match pattern. - BUILD(pattern_elem, case_param, - NODE(TK_SEQ, - NODE(TK_LET, TREE(case_id) TREE(case_type)))); - - ast_append(pattern, pattern_elem); - return true; -} - - -// Handle the given case method parameter, which does not have a type -// specified. -// Check the case parameter and generate the pattern element. -// Returns: true on success, false on error. -static bool param_without_type(ast_t* case_param, ast_t* pattern, - pass_opt_t* opt) -{ - pony_assert(case_param != NULL); - pony_assert(pattern != NULL); - - AST_GET_CHILDREN(case_param, value, type, def_arg); - pony_assert(ast_id(type) == TK_NONE); - - if(ast_id(def_arg) != TK_NONE) - { - ast_error(opt->check.errors, type, - "cannot specify default argument for match value parameter"); - return false; - } - - // Add value to match pattern. - ast_t* value_to_add = ast_dup(value); - - // Value in an expression, need a containing sequence. - BUILD(value_ast, value, NODE(TK_SEQ, TREE(value_to_add))); - ast_append(pattern, value_ast); - - return true; -} - - -// Handle the given case method parameter list. -// Combine and check the case parameters against the existing match parameters -// and generate the match pattern to go in the worker method. -// Returns: match pattern, NULL on error. -static ast_t* process_params(ast_t* match_params, ast_t* case_params, - pass_opt_t* opt) -{ - pony_assert(match_params != NULL); - pony_assert(case_params != NULL); - - bool ok = true; - size_t count = 0; - ast_t* pattern = ast_from(case_params, TK_TUPLE); - ast_t* case_param = ast_child(case_params); - ast_t* match_param = ast_child(match_params); - - while(case_param != NULL && match_param != NULL) - { - AST_GET_CHILDREN(case_param, id, type, def_arg); - - if ((ast_childcount(case_params) == 1) - && (ast_id(id) == TK_REFERENCE) - && (is_name_dontcare(ast_name(ast_child(id))))) - { - ast_error(opt->check.errors, case_param, - "can't have '_' as single parameter in a case method"); - ok = false; - } - else if(ast_id(type) != TK_NONE) - { - // We have a parameter. - if(!param_with_type(case_param, match_param, pattern, opt)) - ok = false; - } - else - { - // We have a value to match. - if(!param_without_type(case_param, pattern, opt)) - ok = false; - } - - case_param = ast_sibling(case_param); - match_param = ast_sibling(match_param); - count++; - } - - if(case_param != NULL || match_param != NULL) - { - ast_error(opt->check.errors, case_params, - "differing number of parameters to case methods"); - ast_error_continue(opt->check.errors, match_params, - "clashing parameter count here"); - ok = false; - } - - if(!ok) - { - ast_free(pattern); - return NULL; - } - - pony_assert(count > 0); - if(count == 1) - { - // Only one parameter, don't need a tuple pattern. - ast_t* tmp = ast_pop(ast_child(pattern)); - ast_free(pattern); - pattern = tmp; - } - - return pattern; -} - - -// Check parameters and build the all parameter structures for a complete set -// of case methods with the given name. -// Generate match operand, worker parameters and worker call arguments. -// Returns: match operand, NULL on failure. -static ast_t* build_params(ast_t* match_params, ast_t* worker_params, - ast_t* call_args, const char* name, pass_opt_t* opt) -{ - pony_assert(match_params != NULL); - pony_assert(worker_params != NULL); - pony_assert(call_args != NULL); - pony_assert(name != NULL); - pony_assert(opt != NULL); - - typecheck_t* t = &opt->check; - pony_assert(t != NULL); - - ast_t* match_operand = ast_from(match_params, TK_TUPLE); - size_t count = 0; - bool ok = true; - - for(ast_t* p = ast_child(match_params); p != NULL; p = ast_sibling(p)) - { - AST_GET_CHILDREN(p, id, type, def_arg); - count++; - - if(ast_id(id) == TK_NONE) - { - pony_assert(ast_id(type) == TK_NONE); - ast_error(opt->check.errors, p, - "name and type not specified for parameter " __zu - " of case function %s", - count, name); - ok = false; - } - else - { - const char* worker_param_name = package_hygienic_id(t); - - // Add parameter to match operand. - BUILD(element, p, - NODE(TK_SEQ, - NODE(TK_CONSUME, - NONE - NODE(TK_REFERENCE, ID(worker_param_name))))); - - ast_append(match_operand, element); - - // Add parameter to worker function. - BUILD(param, p, - NODE(TK_PARAM, - ID(worker_param_name) - TREE(type) - NONE)); // Default argument. - - ast_append(worker_params, param); - - // Add argument to worker call. - BUILD(arg, p, - NODE(TK_SEQ, - NODE(TK_CONSUME, - NONE - NODE(TK_REFERENCE, TREE(id))))); - - ast_append(call_args, arg); - } - } - - if(!ok) - { - ast_free(match_operand); - return NULL; - } - - pony_assert(count > 0); - if(count == 1) - { - // Only one parameter, don't need tuple in operand. - ast_t* tmp = ast_pop(ast_child(match_operand)); - ast_free(match_operand); - match_operand = tmp; - } - - return match_operand; -} - - -// Handle the given case method type parameter. -// Combine and check the case type parameter against the existing match type -// parameter. -// Returns: true on success, false on error. -static bool process_t_param(ast_t* case_param, ast_t* match_param, - pass_opt_t* opt) -{ - pony_assert(case_param != NULL); - pony_assert(match_param != NULL); - - AST_GET_CHILDREN(case_param, case_id, case_constraint, case_def_type); - pony_assert(ast_id(case_id) == TK_ID); - - AST_GET_CHILDREN(match_param, match_id, match_constraint, match_def_type); - pony_assert(ast_id(match_id) == TK_ID || ast_id(match_id) == TK_NONE); - - if(ast_id(match_id) == TK_NONE) - { - // This is the first case method to give this type parameter, just use it - // as given by this case. - ast_replace(&match_id, case_id); - ast_replace(&match_constraint, case_constraint); - ast_replace(&match_def_type, case_def_type); - } - else - { - // Combine new case param with existing match param. - if(ast_name(case_id) != ast_name(match_id)) - { - ast_error(opt->check.errors, case_id, - "type parameter name differs between case methods"); - ast_error_continue(opt->check.errors, match_id, "clashing name here"); - return false; - } - - if(ast_id(case_def_type) != TK_NONE) - { - // This case provides a default argument. - if(ast_id(match_def_type) != TK_NONE) - { - ast_error(opt->check.errors, case_def_type, - "multiple defaults provided for case method type parameter"); - ast_error_continue(opt->check.errors, match_def_type, - "another default defined here"); - return false; - } - - // Move default arg to match parameter. - ast_replace(&match_def_type, case_def_type); - } - - if(ast_id(case_constraint) != TK_NONE) - { - if(ast_id(match_constraint) == TK_NONE) - { - // Case has our first constraint, use it. - ast_replace(&match_constraint, case_constraint); - } - else - { - // Case and existing match both have constraints, intersect them. - REPLACE(&match_constraint, - NODE(TK_ISECTTYPE, - TREE(match_constraint) - TREE(case_constraint))); - } - } - } - - return true; -} - - -// Handle the given case method type parameter list. -// Combine and check the case type parameters against the existing match type -// parameters. -// Returns: true on success, false on error. -static bool process_t_params(ast_t* match_params, ast_t* case_params, - pass_opt_t* opt) -{ - pony_assert(match_params != NULL); - pony_assert(case_params != NULL); - - bool ok = true; - ast_t* case_param = ast_child(case_params); - ast_t* match_param = ast_child(match_params); - - while(case_param != NULL && match_param != NULL) - { - if(!process_t_param(case_param, match_param, opt)) - ok = false; - - case_param = ast_sibling(case_param); - match_param = ast_sibling(match_param); - } - - if(case_param != NULL || match_param != NULL) - { - ast_error(opt->check.errors, case_params, - "differing number of type parameters to case methods"); - ast_error_continue(opt->check.errors, match_params, - "clashing type parameter count here"); - ok = false; - } - - return ok; -} - - -// Check type parameters and build the all type parameter structures for a -// complete set of case methods with the given name. -// Generate type parameter list for worker call. -static void build_t_params(ast_t* match_t_params, - ast_t* worker_t_params) -{ - pony_assert(match_t_params != NULL); - pony_assert(worker_t_params != NULL); - - for(ast_t* p = ast_child(match_t_params); p != NULL; p = ast_sibling(p)) - { - AST_GET_CHILDREN(p, id, constraint, def_type); - pony_assert(ast_id(id) == TK_ID); - - // Add type parameter name to worker call list. - BUILD(type, p, NODE(TK_NOMINAL, NONE TREE(id) NONE NONE NONE)); - ast_append(worker_t_params, type); - ast_setid(worker_t_params, TK_TYPEARGS); - } -} - - -// at this point docstrings might not have been hoisted -static ast_t* get_docstring(ast_t* body, ast_t* doc) -{ - if (doc != NULL && ast_id(doc) == TK_STRING) - return doc; - - if (body != NULL && ast_id(body) == TK_SEQ) - { - ast_t* first = ast_child(body); - if ((first != NULL) && - (ast_id(first) == TK_STRING) && - (ast_sibling(first) != NULL)) - return first; - } - - return NULL; -} - - -static void print_params(ast_t* params, printbuf_t* buf) -{ - bool first = true; - ast_t* param = ast_child(params); - while (param != NULL) - { - if (first) - first = false; - else - printbuf(buf, ", "); - - AST_GET_CHILDREN(param, param_id, param_type); - - switch (ast_id(param_id)) - { - case TK_ID: - case TK_STRING: - printbuf(buf, ast_name(param_id)); - printbuf(buf, ": "); - printbuf(buf, ast_print_type(param_type)); - break; - - case TK_REFERENCE: - printbuf(buf, ast_name(ast_child(param_id))); - break; - - default: - printbuf(buf, ast_get_print(param_id)); - break; - } - - param = ast_sibling(param); - } -} - - -static void add_docstring(ast_t* id, ast_t* match_docstring, - ast_t* case_docstring, ast_t* case_params, ast_t* case_ret) -{ - printbuf_t* buf = printbuf_new(); - - const char* match_ds = ast_name(match_docstring); - size_t m_ds_len = strlen(match_ds); - - const char* case_ds = ast_name(case_docstring); - const size_t c_ds_len = strlen(case_ds); - - const char* id_name = ast_name(id); - - if (c_ds_len > 0) - { - if (m_ds_len > 0) - { - printbuf(buf, match_ds); - printbuf(buf, "\n\n"); - } - - - printbuf(buf, "`"); - printbuf(buf, id_name); - printbuf(buf, "("); - - if (case_params != NULL) - print_params(case_params, buf); - - printbuf(buf, ")"); - if (ast_id(case_ret) != TK_NONE) - { - printbuf(buf, ": "); - printbuf(buf, ast_print_type(case_ret)); - } - - printbuf(buf, "`: "); - printbuf(buf, case_ds); - - ast_set_name(match_docstring, buf->m); - } - - printbuf_free(buf); -} - - -// Add the given case method into the given match method wrapper and check the -// are compatible. -// Returns: match case for worker method or NULL on error. -static ast_t* add_case_method(ast_t* match_method, ast_t* case_method, - pass_opt_t* opt) -{ - pony_assert(match_method != NULL); - pony_assert(case_method != NULL); - - // We need default capabality and return value if not provided explicitly. - if(ast_id(case_method) == TK_FUN) - fun_defaults(case_method); - - AST_GET_CHILDREN(match_method, match_cap, match_id, match_t_params, - match_params, match_ret_type, match_question, match_body, - match_docstring); - - AST_GET_CHILDREN(case_method, case_cap, case_id, case_t_params, case_params, - case_ret_type, case_question, case_body, case_docstring, case_guard); - - bool ok = true; - - if(ast_id(case_method) != ast_id(match_method)) - { - ast_error(opt->check.errors, case_method, - "cannot mix fun and be cases in a single match method"); - ast_error_continue(opt->check.errors, match_method, "clashing method here"); - ok = false; - } - - if(ast_id(case_method) == TK_FUN) - { - if(ast_id(case_cap) != ast_id(match_cap)) - { - ast_error(opt->check.errors, case_cap, - "differing receiver capabilities on case methods"); - ast_error_continue(opt->check.errors, match_cap, - "clashing capability here"); - ok = false; - } - - if(ast_id(match_ret_type) == TK_NONE) - { - // Use case method return type. - ast_replace(&match_ret_type, case_ret_type); - } - else - { - // Union this case method's return type with the existing match one. - REPLACE(&match_ret_type, - NODE(TK_UNIONTYPE, - TREE(match_ret_type) - TREE(case_ret_type))); - } - } - - if(ast_id(case_question) == TK_QUESTION) - // If any case throws the match does too. - ast_setid(match_question, TK_QUESTION); - - if(!process_t_params(match_t_params, case_t_params, opt)) - ok = false; - - ast_t* pattern = process_params(match_params, case_params, opt); - - if(!ok || pattern == NULL) - { - ast_free(pattern); - return NULL; - } - - // Extract case body and guard condition (if any) to avoid copying. - ast_t* body = ast_from(case_body, TK_NONE); - ast_swap(case_body, body); - ast_t* guard = ast_from(case_guard, TK_NONE); - ast_swap(case_guard, guard); - - // concatenate docstring - match_docstring = get_docstring(match_body, match_docstring); - case_docstring = get_docstring(case_body, case_docstring); - - if (match_docstring != NULL && ast_id(match_docstring) == TK_STRING - && case_docstring != NULL && ast_id(case_docstring) == TK_STRING) - { - add_docstring(case_id, match_docstring, case_docstring, case_params, - case_ret_type); - } - - // Make match case. - BUILD(match_case, pattern, - NODE(TK_CASE, AST_SCOPE - TREE(pattern) - TREE(case_guard) - TREE(case_body))); - - return match_case; -} - - -// Handle a set of case methods, starting at the given method. -// Generate wrapper and worker methods and append them to the given list. -// Returns: true on success, false on failure. -static bool sugar_case_method(ast_t* first_case_method, ast_t* members, - const char* name, pass_opt_t* opt) -{ - pony_assert(first_case_method != NULL); - pony_assert(members != NULL); - pony_assert(name != NULL); - pony_assert(opt != NULL); - - typecheck_t* t = &opt->check; - pony_assert(t != NULL); - - ast_t* wrapper = make_match_wrapper(first_case_method, opt); - - if(wrapper == NULL) - return false; - - ast_t* match_cases = ast_from(first_case_method, TK_CASES); - ast_scope(match_cases); - - // Process all methods with the same name. - // We need to remove processed methods. However, removing nodes from a list - // we're iterating over gets rather complex. Instead we mark nodes and remove - // them all in one sweep later. - for(ast_t* p = first_case_method; p != NULL; p = ast_sibling(p)) - { - const char* p_name = ast_name(ast_childidx(p, 1)); - - if(p_name == name) - { - // This method is in the case set. - ast_t* case_ast = add_case_method(wrapper, p, opt); - - ast_setid(p, TK_NONE); // Mark for removal, even in case of errors. - - if(case_ast == NULL) - { - ast_free(wrapper); - ast_free(match_cases); - return false; - } - - ast_append(match_cases, case_ast); - } - } - - // Check params and build match operand, worker parameters and worker call - // arguments. - ast_t* match_params = ast_childidx(wrapper, 3); - ast_t* worker_params = ast_from(match_params, TK_PARAMS); - ast_t* call_args = ast_from(match_params, TK_POSITIONALARGS); - ast_t* match_operand = build_params(match_params, worker_params, call_args, - name, opt); - - if(match_operand == NULL) - { - ast_free(wrapper); - ast_free(match_cases); - ast_free(worker_params); - ast_free(call_args); - return false; - } - - // Complete wrapper function and add to containing entity. - const char* worker_name = package_hygienic_id(t); - ast_t* wrapper_question = ast_childidx(wrapper, 5); - ast_t* wrapper_body = ast_childidx(wrapper, 6); - ast_t* wrapper_call; - ast_t* call_t_args = ast_from(wrapper, TK_NONE); - pony_assert(ast_id(wrapper_body) == TK_SEQ); - pony_assert(ast_childcount(wrapper_body) == 0); - - build_t_params(ast_childidx(wrapper, 2), call_t_args); - - - if(ast_id(call_t_args) == TK_NONE) - { - // No type args needed. - ast_free(call_t_args); - BUILD(tmp, wrapper_body, - NODE(TK_CALL, - NODE(TK_REFERENCE, ID(worker_name)) - TREE(call_args) - NONE - TREE(wrapper_question))); - - wrapper_call = tmp; - } - else - { - // Type args needed on call. - BUILD(tmp, wrapper_body, - NODE(TK_CALL, - NODE(TK_QUALIFY, - NODE(TK_REFERENCE, ID(worker_name)) - TREE(call_t_args)) - TREE(call_args) - NONE - TREE(wrapper_question))); - - wrapper_call = tmp; - } - - ast_append(wrapper_body, wrapper_call); - ast_append(members, wrapper); - - // strip wrapper docstring if empty - ast_t* wrapper_docs = ast_childidx(wrapper, 7); - if (wrapper_docs != NULL && ast_name_len(wrapper_docs) == 0) - ast_setid(wrapper_docs, TK_NONE); - - // Make worker function and add to containing entity. - AST_GET_CHILDREN(wrapper, cap, wrapper_id, t_params, wrapper_params, - ret_type, error); - - if(ast_id(wrapper) == TK_BE) - cap = ast_from(cap, TK_REF); - - BUILD(worker, wrapper, - NODE(TK_FUN, AST_SCOPE - TREE(cap) - ID(worker_name) - TREE(t_params) - TREE(worker_params) - TREE(ret_type) - TREE(error) - NODE(TK_SEQ, - NODE(TK_MATCH, AST_SCOPE - NODE(TK_SEQ, TREE(match_operand)) - TREE(match_cases) - NONE)) // Else clause. - NONE // Doc string. - NONE)); // Guard. - - ast_append(members, worker); - return true; -} - - -// Sugar any case methods in the given entity. -ast_result_t sugar_case_methods(pass_opt_t* opt, ast_t* entity) -{ - pony_assert(opt != NULL); - pony_assert(entity != NULL); - - ast_result_t result = AST_OK; - ast_t* members = ast_childidx(entity, 4); - bool cases_found = false; - - // Check each method to see if its name is repeated, which indicates a case - // method. - for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) - { - if(ast_id(p) == TK_FUN || ast_id(p) == TK_BE) - { - const char* p_name = ast_name(ast_childidx(p, 1)); - - // Check if any subsequent methods share p's name. - for(ast_t* q = ast_sibling(p); q != NULL; q = ast_sibling(q)) - { - if(ast_id(q) == TK_FUN || ast_id(q) == TK_BE) - { - const char* q_name = ast_name(ast_childidx(q, 1)); - - if(q_name == p_name) - { - // p's name is repeated, it's a case method, get a match method. - if(!sugar_case_method(p, members, p_name, opt)) - result = AST_ERROR; - - cases_found = true; - break; - } - } - } - } - } - - if(cases_found) - { - // Remove nodes marked during processing. - ast_t* filtered_members = ast_from(members, TK_MEMBERS); - ast_t* list = NULL; - ast_t* member; - - while((member = ast_pop(members)) != NULL) - { - if(ast_id(member) == TK_NONE) // Marked for removal. - ast_free(member); - else // Put back in filtered list. - ast_list_append(filtered_members, &list, member); - } - - ast_replace(&members, filtered_members); - } - - return result; -} diff --git a/src/libponyc/pass/casemethod.h b/src/libponyc/pass/casemethod.h deleted file mode 100644 index e3ad1c858ac..00000000000 --- a/src/libponyc/pass/casemethod.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef PASS_CASEMETHOD_H -#define PASS_CASEMETHOD_H - -#include -#include "../ast/ast.h" -#include "../pass/pass.h" - -PONY_EXTERN_C_BEGIN - -// Sugar any case methods in the given entity. -ast_result_t sugar_case_methods(pass_opt_t* opt, ast_t* entity); - -PONY_EXTERN_C_END - -#endif diff --git a/src/libponyc/pass/sugar.c b/src/libponyc/pass/sugar.c index a112b050ebd..bdece809a05 100644 --- a/src/libponyc/pass/sugar.c +++ b/src/libponyc/pass/sugar.c @@ -1,5 +1,4 @@ #include "sugar.h" -#include "casemethod.h" #include "../ast/astbuild.h" #include "../ast/id.h" #include "../ast/printbuf.h" @@ -156,6 +155,8 @@ static ast_result_t sugar_module(pass_opt_t* opt, ast_t* ast) static ast_result_t sugar_entity(pass_opt_t* opt, ast_t* ast, bool add_create, token_id def_def_cap) { + (void)opt; + AST_GET_CHILDREN(ast, id, typeparams, defcap, traits, members); if(add_create) @@ -234,8 +235,7 @@ static ast_result_t sugar_entity(pass_opt_t* opt, ast_t* ast, bool add_create, } ast_free_unattached(init_seq); - - return sugar_case_methods(opt, ast); + return AST_OK; } diff --git a/test/libponyc/dontcare.cc b/test/libponyc/dontcare.cc index 03fe785358f..b187be6d85e 100644 --- a/test/libponyc/dontcare.cc +++ b/test/libponyc/dontcare.cc @@ -188,13 +188,3 @@ TEST_F(DontcareTest, CannotUseDontcareInCaseExpression) " end"; TEST_ERRORS_1(src, "can't have a case with `_` only, use an else clause"); } - -TEST_F(DontcareTest, CannotUseDontcareInCaseMethod) -{ - const char* src = - "class C\n" - " fun case_function(x: I32): I32 => -1\n" - " fun case_function(_): I32 => 42\n"; - TEST_ERRORS_1(src, "can't have '_' as single parameter in a case method"); -} - diff --git a/test/libponyc/scope.cc b/test/libponyc/scope.cc index ed40af64ff5..546be12213f 100644 --- a/test/libponyc/scope.cc +++ b/test/libponyc/scope.cc @@ -295,6 +295,17 @@ TEST_F(ScopeTest, CanShadowDontcare) } +TEST_F(ScopeTest, MethodOverloading) +{ + const char* src = + "actor A\n" + " fun foo() => None\n" + " fun foo(a: None) => None"; + + TEST_ERROR(src); +} + + /* TEST_F(ScopeTest, Use) { diff --git a/test/libponyc/sugar.cc b/test/libponyc/sugar.cc index 1f824d51c8b..726ffe69306 100644 --- a/test/libponyc/sugar.cc +++ b/test/libponyc/sugar.cc @@ -1727,723 +1727,6 @@ TEST_F(SugarTest, IfdefPosix) } -TEST_F(SugarTest, CaseFunction) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0): U64 => 0\n" - " fun fib(1): U64 => 1\n" - " fun fib(y: U64): U64 =>\n" - " fib(y - 2) + fib(y - 1)"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib(y: U64): (None | U64 | U64 | U64) =>\n" - " $1(consume y)\n" - " fun box $1($2: U64): (None | U64 | U64 | U64) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | 1 => 1\n" - " | $let y: U64 => fib(y.sub(2)).add(fib(y.sub(1)))\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionPlusOtherFun) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0): U64 => 0\n" - " fun fib(1): U64 => 1\n" - " fun not_fib(): U64 => 1\n" - " fun fib(y: U64): U64 =>\n" - " fib(y - 2) + fib(y - 1)"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box not_fib(): U64 => 1\n" - " fun box fib(y: U64): (None | U64 | U64 | U64) =>\n" - " $1(consume y)\n" - " fun box $1($2: U64): (None | U64 | U64 | U64) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | 1 => 1\n" - " | $let y: U64 => fib(y.sub(2)).add(fib(y.sub(1)))\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunction2InOneClassPlusOtherFun) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun foo(0): U64 => 0\n" - " fun foo(x: U64): U64 => 1\n" - " fun other(): U64 => 1\n" - " fun bar(0): U32 => 0\n" - " fun bar(y: U32): U32 => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box other(): U64 => 1\n" - " fun box foo(x: U64): (None | U64 | U64) =>\n" - " $1(consume x)\n" - " fun box $1($2: U64): (None | U64 | U64) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let x: U64 => 1\n" - " end\n" - " fun box bar(y: U32): (None | U32 | U32) =>\n" - " $3(consume y)\n" - " fun box $3($4: U32): (None | U32 | U32) =>\n" - " match consume $4\n" - " | 0 => 0\n" - " | $let y: U32 => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionParamNamedTwice) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0): U64 => 0\n" - " fun fib(y: U64): U64 => 1\n" - " fun fib(y: U64): U64 => 2"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib(y: (U64 | U64)): (None | U64 | U64 | U64) =>\n" - " $1(consume y)\n" - " fun box $1($2: (U64 | U64)): (None | U64 | U64 | U64) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let y: U64 => 1\n" - " | $let y: U64 => 2\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunction2Params) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0, 0): U64 => 0\n" - " fun fib(a: U64, b: U32): U64 => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib(a: U64, b: U32): (None | U64 | U64) =>\n" - " $1(consume a, consume b)\n" - " fun box $1($2: U64, $3: U32): (None | U64 | U64) =>\n" - " match (consume $2, consume $3)\n" - " | (0, 0) => 0\n" - " | ($let a: U64, $let b: U32) => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionParamTypeUnion) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(a: U32): U64 => 0\n" - " fun fib(a: U64): U64 => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib(a: (U32 | U64)): (None | U64 | U64) =>\n" - " $1(consume a)\n" - " fun box $1($2: (U32 | U64)): (None | U64 | U64) =>\n" - " match consume $2\n" - " | $let a: U32 => 0\n" - " | $let a: U64 => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionReturnUnion) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0): U64 => 0\n" - " fun fib(a: U64): U32 => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib(a: U64): (None | U64 | U32) =>\n" - " $1(consume a)\n" - " fun box $1($2: U64): (None | U64 | U32) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let a: U64 => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionCap) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun ref fib(0): U64 => 0\n" - " fun ref fib(a: U64): U64 => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun ref fib(a: U64): (None | U64 | U64) =>\n" - " $1(consume a)\n" - " fun ref $1($2: U64): (None | U64 | U64) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let a: U64 => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionCapClash) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun box fib(0): U64 => 0\n" - " fun ref fib(a: U64): U64 => 1"; - - TEST_ERROR(short_form); -} - - -TEST_F(SugarTest, CaseFunctionOneErrors) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun ref fib(0): U64 ? => 0\n" - " fun ref fib(a: U64): U64 => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun ref fib(a: U64): (None | U64 | U64) ? =>\n" - " $1(consume a)?\n" - " fun ref $1($2: U64): (None | U64 | U64) ? =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let a: U64 => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionAllError) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun ref fib(0): U64 ? => 0\n" - " fun ref fib(a: U64): U64 ? => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun ref fib(a: U64): (None | U64 | U64) ? =>\n" - " $1(consume a)?\n" - " fun ref $1($2: U64): (None | U64 | U64) ? =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let a: U64 => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionParamCountClash) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0): U64 => 0\n" - " fun fib(a: U64, b: U32): U64 => 1"; - - TEST_ERROR(short_form); -} - - -TEST_F(SugarTest, CaseFunctionNoParams) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(): U64 => 0\n" - " fun fib(): U64 => 1"; - - TEST_ERROR(short_form); -} - - -TEST_F(SugarTest, CaseFunctionParamNameClash) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(a: U64): U64 => 0\n" - " fun fib(b: U64): U64 => 1"; - - TEST_ERROR(short_form); -} - - -TEST_F(SugarTest, CaseFunctionParamNotNamed) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0): U64 => 0\n" - " fun fib(1): U64 => 1"; - - TEST_ERROR(short_form); -} - - -TEST_F(SugarTest, CaseFunctionValuePlusTypeBad) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0: U64): U64 => 0\n" - " fun fib(a: U64): U64 => 1"; - - TEST_ERROR(short_form); -} - - -TEST_F(SugarTest, CaseFunctionValuePlusDefaultArgBad) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0 = 0): U64 => 0\n" - " fun fib(a: U64): U64 => 1"; - - TEST_ERROR(short_form); -} - - -TEST_F(SugarTest, CaseFunctionDontCare) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0, _): U64 => 0\n" - " fun fib(a: U64, b: U32): U64 => 1\n" - " fun fib(_, _): U64 => 2"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib(a: U64, b: U32): (None | U64 | U64 | U64) =>\n" - " $1(consume a, consume b)\n" - " fun box $1($2: U64, $3: U32): (None | U64 | U64 | U64) =>\n" - " match (consume $2, consume $3)\n" - " | (0, _) => 0\n" - " | ($let a: U64, $let b: U32) => 1\n" - " | (_, _) => 2\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionGuard) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0): U64 => 0\n" - " fun fib(a: U64): U64 if a > 3 => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib(a: U64): (None | U64 | U64) =>\n" - " $1(consume a)\n" - " fun box $1($2: U64): (None | U64 | U64) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let a: U64 if a.gt(3) => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionDefaultValue) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0): U64 => 0\n" - " fun fib(a: U64 = 4): U64 => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib(a: U64 = 4): (None | U64 | U64) =>\n" - " $1(consume a)\n" - " fun box $1($2: U64): (None | U64 | U64) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let a: U64 => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionDefaultValueClash) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0 = 4): U64 => 0\n" - " fun fib(a: U64 = 4): U64 => 1"; - - TEST_ERROR(short_form); -} - - -TEST_F(SugarTest, CaseBehaviour) -{ - const char* short_form = - "actor Foo\n" - " var create: U32\n" - " be fib(0) => 0\n" - " be fib(a: U64) => 1"; - - const char* full_form = - "use \"builtin\"\n" - "actor tag Foo\n" - " var create: U32\n" - " be tag fib(a: U64): None =>\n" - " $1(consume a)\n" - " fun ref $1($2: U64): None =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let a: U64 => 1\n" - " end\n" - " None"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionBehaviourClash) -{ - const char* short_form = - "actor Foo\n" - " var create: U32\n" - " be fib(0) => 0\n" - " fun fib(a: U64) => 1"; - - TEST_ERROR(short_form); -} - - -TEST_F(SugarTest, CaseFunctionConstructorsFail) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " new fib(0) => create = 0\n" - " new fib(a: U64) => create = 1"; - - TEST_ERROR(short_form); -} - - -TEST_F(SugarTest, CaseFunctionTypeParam) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib[A](0): A => 0\n" - " fun fib[A](a: U64): A => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib[A: A](a: U64): (None | A | A) =>\n" - " $1[A](consume a)\n" - " fun box $1[A: A]($2: U64): (None | A | A) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let a: U64 => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunction2TypeParams) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib[A, B](0): U64 => 0\n" - " fun fib[A, B](a: U64): U64 => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib[A: A, B: B](a: U64): (None | U64 | U64) =>\n" - " $1[A, B](consume a)\n" - " fun box $1[A: A, B: B]($2: U64): (None | U64 | U64) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let a: U64 => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionTypeParamConstraint) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib[A](0): U64 => 0\n" - " fun fib[A: B](a: U64): U64 => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib[A: B](a: U64): (None | U64 | U64) =>\n" - " $1[A](consume a)\n" - " fun box $1[A: B]($2: U64): (None | U64 | U64) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let a: U64 => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionTypeParamConstraintIntersect) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib[A: B](0): U64 => 0\n" - " fun fib[A: C](a: U64): U64 => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib[A: (B & C)](a: U64): (None | U64 | U64) =>\n" - " $1[A](consume a)\n" - " fun box $1[A: (B & C)]($2: U64): (None | U64 | U64) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let a: U64 => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionTypeParamCountClash) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib(0): U64 => 0\n" - " fun fib[A](a: U64): U64 => 1"; - - TEST_ERROR(short_form); -} - - -TEST_F(SugarTest, CaseFunctionTypeParamNameClash) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib[A](a: U64): U64 => 0\n" - " fun fib[B](b: U64): U64 => 1"; - - TEST_ERROR(short_form); -} - - - -TEST_F(SugarTest, CaseFunctionDefaultTypeParam) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib[A](0): U64 => 0\n" - " fun fib[A = B](a: U64): U64 => 1"; - - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box fib[A: A = B](a: U64): (None | U64 | U64) =>\n" - " $1[A](consume a)\n" - " fun box $1[A: A = B]($2: U64): (None | U64 | U64) =>\n" - " match consume $2\n" - " | 0 => 0\n" - " | $let a: U64 => 1\n" - " end"; - - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionDefaultTypeParamClash) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun fib[A = B](0): U64 => 0\n" - " fun fib[A = C](a: U64): U64 => 1"; - - TEST_ERROR(short_form); -} - -TEST_F(SugarTest, CaseFunctionDocStringMergeSingleStringBody) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun name(a: U64): String => \"dunno\"\n" - " fun name(1): String => \"John Doe\"\n"; - const char* full_form = - "use \"builtin\"\n" - "class ref Foo\n" - " var create: U32\n" - " fun box name(a: U64): (None|String|String) =>\n" - " $1(consume a)\n" - " fun box $1($2: U64): (None|String|String) =>\n" - " match consume $2\n" - " | $let a: U64 => \"dunno\"\n" - " | 1 => \"John Doe\"\n" - " end"; - TEST_EQUIV(short_form, full_form); -} - - -TEST_F(SugarTest, CaseFunctionDocStringMerge) -{ - const char* short_form = - "class Foo\n" - " var create: U32\n" - " fun with_docstring(0): U64 =>\n" - " \"\"\"\n" - " exit case\n" - " \"\"\"\n" - " 0\n" - " fun with_docstring(a : U64): U64 if a > 4 =>\n" - " \"\"\"\n" - " bigger than four\n" - " \"\"\"\n" - " a-1\n" - " fun with_docstring(2): U64 =>\n" - " \"\"\"\n" - " dont care\n" - " \"\"\"\n" - " 1\n"; - TEST_COMPILE(short_form); - ast_t* m = module; - AST_GET_CHILDREN(m, use_decls, type_decls); - - ast_t* foo_ast = type_decls; - ASSERT_TRUE(foo_ast != NULL); - ASSERT_EQ(ast_id(foo_ast), TK_CLASS); - AST_GET_CHILDREN(foo_ast, id, type_params, cap, provides, members); - - ASSERT_TRUE(members != NULL); - - AST_GET_CHILDREN(members, fields, methods); - - ast_t* method = methods; - const char* method_name = "with_docstring"; - while (method != NULL) - { - ast_t* child = ast_childidx(method, 1); - - if (strncmp(ast_name(child), method_name, ast_name_len(child)) == 0) - break; - method = ast_sibling(method); - } - - ASSERT_TRUE(method != NULL); - AST_GET_CHILDREN(method, mcap, mid, mtype_params, mparams, mreturn_type, - merror, mbody, mdocstring); - ASSERT_STREQ( - ast_get_print(mdocstring), - "`with_docstring(0): U64`: exit case\n" - "\n" - "\n" - "`with_docstring(a: U64): U64`: bigger than four\n" - "\n" - "\n" - "`with_docstring(2): U64`: dont care\n"); -} - - TEST_F(SugarTest, AsOperatorWithLambdaType) { const char* short_form =