diff --git a/src/libponyc/ast/frame.c b/src/libponyc/ast/frame.c index 817ebe3ba8..662e976c5d 100644 --- a/src/libponyc/ast/frame.c +++ b/src/libponyc/ast/frame.c @@ -121,7 +121,7 @@ bool frame_push(typecheck_t* t, ast_t* ast) { case TK_TYPEPARAM: { - AST_GET_CHILDREN(parent, id, constraint, def_type); + AST_GET_CHILDREN(parent, id, unbounded_type_params, constraint, def_type); if(constraint == ast) { diff --git a/src/libponyc/ast/lexer.c b/src/libponyc/ast/lexer.c index 70134fd378..53a6795b72 100644 --- a/src/libponyc/ast/lexer.c +++ b/src/libponyc/ast/lexer.c @@ -258,8 +258,11 @@ static const lextoken_t abstract[] = { "branch", TK_LITERALBRANCH }, { "opliteral", TK_OPERATORLITERAL }, + { "_", TK_UNDERSCORE }, { "typeparams", TK_TYPEPARAMS }, { "typeparam", TK_TYPEPARAM }, + { "unboundtypeparams", TK_UNBOUNDTYPEPARAMS }, + { "unboundtypeparam", TK_UNBOUNDTYPEPARAM }, { "valueformalparam", TK_VALUEFORMALPARAM }, { "params", TK_PARAMS }, { "param", TK_PARAM }, diff --git a/src/libponyc/ast/parser.c b/src/libponyc/ast/parser.c index d5e6027d25..3f58f6b33a 100644 --- a/src/libponyc/ast/parser.c +++ b/src/libponyc/ast/parser.c @@ -122,10 +122,28 @@ DEF(typearg); RULE("type argument", type, typeargliteral, typeargconst); DONE(); -// ID [COLON type] [ASSIGN typearg] +// UNDERSCORE +DEF(underscore); + AST_NODE(TK_UNBOUNDTYPEPARAM); + TOKEN("_", TK_ID); + IF(TK_COLON, RULE("type constraint", type)); + IF(TK_ASSIGN, RULE("default type argument", typearg)); + DONE(); + +// LSQUARE UNDERSCORE {COMMA UNDERSCORE} RSQUARE +DEF(unboundtypeparams); + AST_NODE(TK_UNBOUNDTYPEPARAMS); + SKIP(NULL, TK_LSQUARE, TK_LSQUARE_NEW); + RULE("underscore", underscore); + WHILE(TK_COMMA, RULE("underscore", underscore)); + TERMINATE("higher kinded type arguments", TK_RSQUARE); + DONE(); + +// ID [UNDERSCORE {COMMA UNDERSCORE}] [COLON type] [ASSIGN typearg] DEF(typeparam); AST_NODE(TK_TYPEPARAM); TOKEN("name", TK_ID); + OPT RULE("higher kinded type parameters", unboundtypeparams); IF(TK_COLON, RULE("type constraint", type)); IF(TK_ASSIGN, RULE("default type argument", typearg)); DONE(); diff --git a/src/libponyc/ast/token.c b/src/libponyc/ast/token.c index a838532bdf..75683033dd 100644 --- a/src/libponyc/ast/token.c +++ b/src/libponyc/ast/token.c @@ -177,6 +177,9 @@ const char* token_print(token_t* token) return token->printed; } + case TK_UNDERSCORE: + return "_"; + case TK_LEX_ERROR: return "LEX_ERROR"; diff --git a/src/libponyc/ast/token.h b/src/libponyc/ast/token.h index 8592a18af2..502cefd4c3 100644 --- a/src/libponyc/ast/token.h +++ b/src/libponyc/ast/token.h @@ -39,6 +39,7 @@ typedef enum token_id TK_LSQUARE, TK_RSQUARE, TK_BACKSLASH, + TK_UNDERSCORE, TK_COMMA, TK_ARROW, @@ -240,6 +241,8 @@ typedef enum token_id TK_PACKAGEREF, TK_TYPEREF, TK_TYPEPARAMREF, + TK_UNBOUNDTYPEPARAM, + TK_UNBOUNDTYPEPARAMS, TK_NEWREF, TK_NEWBEREF, TK_BEREF, diff --git a/src/libponyc/ast/treecheckdef.h b/src/libponyc/ast/treecheckdef.h index d4125d75f0..8bb70cf050 100644 --- a/src/libponyc/ast/treecheckdef.h +++ b/src/libponyc/ast/treecheckdef.h @@ -85,9 +85,20 @@ RULE(method, RULE(type_params, ONE_OR_MORE(type_param), TK_TYPEPARAMS); +RULE(unboundtypeparam, + CHILD(id) + CHILD(type, none) // Constraint + CHILD(type, none), // Default + TK_UNBOUNDTYPEPARAM); + +RULE(unboundtypeparams, + ONE_OR_MORE(unboundtypeparam), + TK_UNBOUNDTYPEPARAMS); + RULE(type_param, HAS_DATA // Original typeparam definition CHILD(id) + CHILD(unboundtypeparams, none) CHILD(type, none) // Constraint CHILD(type, none), // Default TK_TYPEPARAM); diff --git a/src/libponyc/expr/array.c b/src/libponyc/expr/array.c index e8f30e93e9..e88464cc6c 100644 --- a/src/libponyc/expr/array.c +++ b/src/libponyc/expr/array.c @@ -176,7 +176,7 @@ static void find_possible_element_types(pass_opt_t* opt, ast_t* ast, { ast_t* def = (ast_t*)ast_data(ast); pony_assert(ast_id(def) == TK_TYPEPARAM); - find_possible_element_types(opt, ast_childidx(def, 1), list); + find_possible_element_types(opt, ast_childidx(def, 2), list); return; } @@ -217,7 +217,7 @@ static void find_possible_iterator_element_types(pass_opt_t* opt, ast_t* ast, { ast_t* def = (ast_t*)ast_data(ast); pony_assert(ast_id(def) == TK_TYPEPARAM); - find_possible_iterator_element_types(opt, ast_childidx(def, 1), list); + find_possible_iterator_element_types(opt, ast_childidx(def, 2), list); return; } diff --git a/src/libponyc/expr/literal.c b/src/libponyc/expr/literal.c index 1f3aec69f0..0de9ecc878 100644 --- a/src/libponyc/expr/literal.c +++ b/src/libponyc/expr/literal.c @@ -264,7 +264,7 @@ static int uifset_formal_param(pass_opt_t* opt, ast_t* type_param_ref, pony_assert(ast_id(type_param) == TK_TYPEPARAM); pony_assert(chain != NULL); - ast_t* constraint = ast_childidx(type_param, 1); + ast_t* constraint = ast_childidx(type_param, 2); pony_assert(constraint != NULL); // If the constraint is not a subtype of (Real[A] & Number) then there are no diff --git a/src/libponyc/pass/names.c b/src/libponyc/pass/names.c index a91be727fa..a1ac48060c 100644 --- a/src/libponyc/pass/names.c +++ b/src/libponyc/pass/names.c @@ -174,13 +174,6 @@ static bool names_typeparam(pass_opt_t* opt, ast_t** astp, ast_t* def) AST_GET_CHILDREN(ast, package, id, typeargs, cap, ephemeral); pony_assert(ast_id(package) == TK_NONE); - if(ast_id(typeargs) != TK_NONE) - { - ast_error(opt->check.errors, typeargs, - "can't qualify a type parameter with type arguments"); - return false; - } - if(ast_id(ast) == TK_NOMINAL) { ast_t* module = ast_nearest(ast, TK_MODULE); if(module && ast_get(module, ast_name(id), 0)) { diff --git a/src/libponyc/pass/sugar.c b/src/libponyc/pass/sugar.c index 6633c8cd20..f108ad34e0 100644 --- a/src/libponyc/pass/sugar.c +++ b/src/libponyc/pass/sugar.c @@ -304,7 +304,7 @@ static ast_result_t sugar_entity(pass_opt_t* opt, ast_t* ast, bool add_create, static ast_result_t sugar_typeparam(ast_t* ast) { - AST_GET_CHILDREN(ast, id, constraint); + AST_GET_CHILDREN(ast, id, unbound_type_param, constraint); if(ast_id(constraint) == TK_NONE) { diff --git a/src/libponyc/type/lookup.c b/src/libponyc/type/lookup.c index f0b75bda08..b05ee8f9da 100644 --- a/src/libponyc/type/lookup.c +++ b/src/libponyc/type/lookup.c @@ -236,7 +236,7 @@ static deferred_reification_t* lookup_typeparam(pass_opt_t* opt, ast_t* from, ast_t* orig, ast_t* type, const char* name, bool errors, bool allow_private) { ast_t* def = (ast_t*)ast_data(type); - ast_t* constraint = ast_childidx(def, 1); + ast_t* constraint = ast_childidx(def, 2); ast_t* constraint_def = (ast_t*)ast_data(constraint); if(def == constraint_def) diff --git a/src/libponyc/type/reify.c b/src/libponyc/type/reify.c index 3b4b532b3e..0446fb2b1a 100644 --- a/src/libponyc/type/reify.c +++ b/src/libponyc/type/reify.c @@ -163,7 +163,7 @@ bool reify_defaults(ast_t* typeparams, ast_t* typeargs, bool errors, while(typeparam != NULL) { - ast_t* defarg = ast_childidx(typeparam, 2); + ast_t* defarg = ast_childidx(typeparam, 3); if(ast_id(defarg) == TK_NONE) break; @@ -424,7 +424,7 @@ bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs, } // Reify the constraint. - ast_t* constraint = ast_childidx(typeparam, 1); + ast_t* constraint = ast_childidx(typeparam, 2); ast_t* r_constraint = reify(constraint, typeparams, typeargs, opt, true); diff --git a/src/libponyc/type/sanitise.c b/src/libponyc/type/sanitise.c index 5ef91fd7f9..c42542b778 100644 --- a/src/libponyc/type/sanitise.c +++ b/src/libponyc/type/sanitise.c @@ -9,7 +9,7 @@ static void collect_type_param(ast_t* orig_param, ast_t* params, ast_t* args) pony_assert(orig_param != NULL); // Get original type parameter info - AST_GET_CHILDREN(orig_param, id, constraint, deflt); + AST_GET_CHILDREN(orig_param, id, unbound_type_params, constraint, deflt); const char* name = ast_name(id); constraint = sanitise_type(constraint); @@ -21,6 +21,7 @@ static void collect_type_param(ast_t* orig_param, ast_t* params, ast_t* args) BUILD(new_param, orig_param, NODE(TK_TYPEPARAM, ID(name) + TREE(unbound_type_params) TREE(constraint) NONE)); diff --git a/src/libponyc/type/subtype.c b/src/libponyc/type/subtype.c index 2af8bdf4fa..ebc64040aa 100644 --- a/src/libponyc/type/subtype.c +++ b/src/libponyc/type/subtype.c @@ -313,8 +313,8 @@ static bool is_reified_fun_sub_fun(ast_t* sub, ast_t* super, while((sub_typeparam != NULL) && (super_typeparam != NULL)) { - ast_t* sub_constraint = ast_childidx(sub_typeparam, 1); - ast_t* super_constraint = ast_childidx(super_typeparam, 1); + ast_t* sub_constraint = ast_childidx(sub_typeparam, 2); + ast_t* super_constraint = ast_childidx(super_typeparam, 2); if(!is_x_sub_x(super_constraint, sub_constraint, CHECK_CAP_EQ, errorf, opt)) diff --git a/src/libponyc/type/typeparam.c b/src/libponyc/type/typeparam.c index c0d08f726e..907ed6528f 100644 --- a/src/libponyc/type/typeparam.c +++ b/src/libponyc/type/typeparam.c @@ -333,6 +333,10 @@ static token_id cap_from_constraint(ast_t* type) case TK_TYPEPARAMREF: return cap_from_constraint(typeparam_constraint(type)); + // TODO: fix this! + case TK_UNBOUNDTYPEPARAM: + return cap_from_constraint(typeparam_constraint(type)); + default: {} } @@ -503,7 +507,7 @@ ast_t* typeparam_constraint(ast_t* typeparamref) { pony_assert(ast_id(typeparamref) == TK_TYPEPARAMREF); ast_t* def = (ast_t*)ast_data(typeparamref); - ast_t* constraint = ast_childidx(def, 1); + ast_t* constraint = ast_childidx(def, 2); astlist_t* def_list = astlist_push(NULL, def); while(ast_id(constraint) == TK_TYPEPARAMREF) @@ -517,7 +521,7 @@ ast_t* typeparam_constraint(ast_t* typeparamref) } def_list = astlist_push(def_list, constraint_def); - constraint = ast_childidx(constraint_def, 1); + constraint = ast_childidx(constraint_def, 2); } astlist_free(def_list); diff --git a/test/libponyc/badpony.cc b/test/libponyc/badpony.cc index 7d966a607e..0e6b8a9ac8 100644 --- a/test/libponyc/badpony.cc +++ b/test/libponyc/badpony.cc @@ -69,6 +69,19 @@ TEST_F(BadPonyTest, TypeParamMissingForTypeInProvidesList) TEST_ERRORS_1(src, "not enough type arguments"); } +TEST_F(BadPonyTest, HKTMustBeReturnedReified) +{ + const char* src = + "trait Foo[F[_]]\n" + " fun f[A](): F // F has not been exhaustively parameterized and thus cannot be reified. Should be F[A]\n" + + "actor Main\n" + "new create(env: Env) => None"; + + + TEST_ERRORS_1(src, "not enough type arguments"); +} + TEST_F(BadPonyTest, TupleIndexIsZero) { // From issue #397 diff --git a/test/libponyc/parse_entity.cc b/test/libponyc/parse_entity.cc index 8cb4bc84a9..eb389f63a3 100644 --- a/test/libponyc/parse_entity.cc +++ b/test/libponyc/parse_entity.cc @@ -40,6 +40,20 @@ TEST_F(ParseEntityTest, ActorMinimal) } +TEST_F(ParseEntityTest, ActorMinimalGeneric) +{ + const char* src = "actor Foo[A]"; + + TEST_COMPILE(src); +} + +TEST_F(ParseEntityTest, ActorMinimalHKT) +{ + const char* src = "actor Foo[F[_]]"; + + TEST_COMPILE(src); +} + TEST_F(ParseEntityTest, ActorCApi) { const char* src = "actor @ Foo"; diff --git a/test/libponyc/scope.cc b/test/libponyc/scope.cc index 546be12213..13193bf83c 100644 --- a/test/libponyc/scope.cc +++ b/test/libponyc/scope.cc @@ -166,6 +166,18 @@ TEST_F(ScopeTest, TypeParam) } +TEST_F(ScopeTest, HigherKindedTypeParamMethod) +{ + const char* src = "actor A fun foo[F[_]]() => None"; + + TEST_COMPILE(src); + + ast_t* foo = lookup_member("A", "foo"); + ASSERT_ID(TK_FUN, foo); + ASSERT_ID(TK_TYPEPARAM, lookup_in(foo, "F")); + ASSERT_EQ(1, ref_count(package, "F")); +} + TEST_F(ScopeTest, Local) { const char* src = "actor A fun foo() => var bar: U32 = 3";