Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix support for recursive constraints in iftype conditions. #1961

Merged
merged 4 commits into from
Jul 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/libponyc/ast/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ static const lextoken_t keywords[] =

{ "if", TK_IF },
{ "ifdef", TK_IFDEF },
{ "iftype", TK_IFTYPE },
{ "iftype", TK_IFTYPE_SET },
{ "then", TK_THEN },
{ "else", TK_ELSE },
{ "elseif", TK_ELSEIF },
Expand Down
37 changes: 20 additions & 17 deletions src/libponyc/ast/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -738,31 +738,34 @@ DEF(ifdef);
REORDER(0, 2, 3, 1);
DONE();

// ELSEIF [annotations] type <: type THEN seq [elseiftype | (ELSE seq)]
DEF(elseiftype);
// type <: type THEN seq
DEF(iftype);
AST_NODE(TK_IFTYPE);
SCOPE();
SKIP(NULL, TK_ELSEIF);
ANNOTATE(annotations);
RULE("type", type);
SKIP(NULL, TK_SUBTYPE);
RULE("type", type);
SKIP(NULL, TK_THEN);
RULE("then value", seq);
DONE();

// ELSEIF [annotations] iftype [elseiftype | (ELSE seq)]
DEF(elseiftype);
AST_NODE(TK_IFTYPE_SET);
SKIP(NULL, TK_ELSEIF);
ANNOTATE(annotations);
SCOPE();
RULE("iftype clause", iftype);
OPT RULE("else clause", elseiftype, elseclause);
DONE();

// IFTYPE [annotations] type <: type THEN seq [elseiftype | (ELSE seq)] END
DEF(iftype);
// IFTYPE_SET [annotations] iftype [elseiftype | (ELSE seq)] END
DEF(iftypeset);
PRINT_INLINE();
TOKEN(NULL, TK_IFTYPE);
ANNOTATE(annotations);
TOKEN(NULL, TK_IFTYPE_SET);
SCOPE();
RULE("type", type);
SKIP(NULL, TK_SUBTYPE);
RULE("type", type);
SKIP(NULL, TK_THEN);
RULE("then value", seq);
ANNOTATE(annotations);
RULE("iftype clause", iftype);
OPT RULE("else clause", elseiftype, elseclause);
TERMINATE("iftype expression", TK_END);
DONE();
Expand Down Expand Up @@ -965,18 +968,18 @@ DEF(test_ifdef_flag);
TOKEN(NULL, TK_ID);
DONE();

// cond | ifdef | iftype | match | whileloop | repeat | forloop | with | try |
// cond | ifdef | iftypeset | match | whileloop | repeat | forloop | with | try |
// recover | consume | pattern | const_expr | test_<various>
DEF(term);
RULE("value", cond, ifdef, iftype, match, whileloop, repeat, forloop, with,
RULE("value", cond, ifdef, iftypeset, match, whileloop, repeat, forloop, with,
try_block, recover, consume, pattern, const_expr, test_noseq,
test_seq_scope, test_try_block, test_ifdef_flag, test_prefix);
DONE();

// cond | ifdef | iftype | match | whileloop | repeat | forloop | with | try |
// cond | ifdef | iftypeset | match | whileloop | repeat | forloop | with | try |
// recover | consume | pattern | const_expr | test_<various>
DEF(nextterm);
RULE("value", cond, ifdef, iftype, match, whileloop, repeat, forloop, with,
RULE("value", cond, ifdef, iftypeset, match, whileloop, repeat, forloop, with,
try_block, recover, consume, nextpattern, const_expr, test_noseq,
test_seq_scope, test_try_block, test_ifdef_flag, test_prefix);
DONE();
Expand Down
1 change: 1 addition & 0 deletions src/libponyc/ast/token.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ typedef enum token_id
TK_IF,
TK_IFDEF,
TK_IFTYPE,
TK_IFTYPE_SET,
TK_THEN,
TK_ELSE,
TK_ELSEIF,
Expand Down
5 changes: 3 additions & 2 deletions src/libponyc/codegen/gencontrol.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,11 @@ LLVMValueRef gen_if(compile_t* c, ast_t* ast)

LLVMValueRef gen_iftype(compile_t* c, ast_t* ast)
{
AST_GET_CHILDREN(ast, subtype, supertype, left, right);
AST_GET_CHILDREN(ast, left, right);
AST_GET_CHILDREN(left, subtype, supertype, body);

if(is_subtype_constraint(subtype, supertype, NULL, c->opt))
return gen_expr(c, left);
return gen_expr(c, body);

return gen_expr(c, right);
}
Expand Down
2 changes: 1 addition & 1 deletion src/libponyc/codegen/genexpr.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ LLVMValueRef gen_expr(compile_t* c, ast_t* ast)
ret = gen_if(c, ast);
break;

case TK_IFTYPE:
case TK_IFTYPE_SET:
ret = gen_iftype(c, ast);
break;

Expand Down
5 changes: 3 additions & 2 deletions src/libponyc/expr/control.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@ bool expr_if(pass_opt_t* opt, ast_t* ast)

bool expr_iftype(pass_opt_t* opt, ast_t* ast)
{
pony_assert(ast_id(ast) == TK_IFTYPE);
AST_GET_CHILDREN(ast, sub, super, left, right);
pony_assert(ast_id(ast) == TK_IFTYPE_SET);
AST_GET_CHILDREN(ast, left_control, right);
AST_GET_CHILDREN(left_control, sub, super, left);

ast_t* type = NULL;

Expand Down
6 changes: 4 additions & 2 deletions src/libponyc/pass/expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ bool is_result_needed(ast_t* ast)
return is_result_needed(parent);

case TK_IFTYPE:
// Sub/supertype not needed, body/else needed only if parent needed.
// Sub/supertype not needed, body needed only if parent needed.
if((ast_child(parent) == ast) || (ast_childidx(parent, 1) == ast))
return false;

Expand All @@ -78,6 +78,7 @@ bool is_result_needed(ast_t* ast)
return is_result_needed(parent);

case TK_CASES:
case TK_IFTYPE_SET:
case TK_TRY:
case TK_TRY_NO_CHECK:
case TK_RECOVER:
Expand Down Expand Up @@ -154,6 +155,7 @@ bool is_method_result(typecheck_t* t, ast_t* ast)
break;

case TK_CASES:
case TK_IFTYPE_SET:
case TK_RECOVER:
// These can be results.
break;
Expand Down Expand Up @@ -254,7 +256,7 @@ ast_result_t pass_expr(ast_t** astp, pass_opt_t* options)
case TK_CALL: r = expr_call(options, astp); break;
case TK_IFDEF:
case TK_IF: r = expr_if(options, ast); break;
case TK_IFTYPE: r = expr_iftype(options, ast); break;
case TK_IFTYPE_SET: r = expr_iftype(options, ast); break;
case TK_WHILE: r = expr_while(options, ast); break;
case TK_REPEAT: r = expr_repeat(options, ast); break;
case TK_TRY_NO_CHECK:
Expand Down
9 changes: 6 additions & 3 deletions src/libponyc/pass/refer.c
Original file line number Diff line number Diff line change
Expand Up @@ -935,8 +935,10 @@ static bool refer_if(pass_opt_t* opt, ast_t* ast)
static bool refer_iftype(pass_opt_t* opt, ast_t* ast)
{
(void)opt;
pony_assert(ast_id(ast) == TK_IFTYPE);
AST_GET_CHILDREN(ast, sub, super, left, right);
pony_assert(ast_id(ast) == TK_IFTYPE_SET);

AST_GET_CHILDREN(ast, left_clause, right);
AST_GET_CHILDREN(left_clause, sub, super, left);

size_t branch_count = 0;

Expand Down Expand Up @@ -1289,7 +1291,8 @@ ast_result_t pass_refer(ast_t** astp, pass_opt_t* options)
case TK_SEQ: r = refer_seq(options, ast); break;
case TK_IFDEF:
case TK_IF: r = refer_if(options, ast); break;
case TK_IFTYPE: r = refer_iftype(options, ast); break;
case TK_IFTYPE_SET:
r = refer_iftype(options, ast); break;
case TK_WHILE: r = refer_while(options, ast); break;
case TK_REPEAT: r = refer_repeat(options, ast); break;
case TK_MATCH: r = refer_match(options, ast); break;
Expand Down
16 changes: 9 additions & 7 deletions src/libponyc/pass/scope.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,30 +193,32 @@ static ast_t* make_iftype_typeparam(pass_opt_t* opt, ast_t* subtype,
TREE(new_constraint)
NONE));

ast_setdata(typeparam, typeparam);
// keep data pointing to the original def
ast_setdata(typeparam, ast_data(def));

return typeparam;
}

static ast_result_t scope_iftype(pass_opt_t* opt, ast_t* ast)
{
AST_GET_CHILDREN(ast, subtype, supertype, then_clause);
pony_assert(ast_id(ast) == TK_IFTYPE);

AST_GET_CHILDREN(ast, subtype, supertype);

ast_t* typeparams = ast_from(ast, TK_TYPEPARAMS);

switch(ast_id(subtype))
{
case TK_NOMINAL:
{
ast_t* typeparam = make_iftype_typeparam(opt, subtype, supertype,
then_clause);
ast_t* typeparam = make_iftype_typeparam(opt, subtype, supertype, ast);
if(typeparam == NULL)
{
ast_free_unattached(typeparams);
return AST_ERROR;
}

if(!set_scope(opt, then_clause, ast_child(typeparam), typeparam, true))
if(!set_scope(opt, ast, ast_child(typeparam), typeparam, true))
{
ast_free_unattached(typeparams);
return AST_ERROR;
Expand Down Expand Up @@ -251,14 +253,14 @@ static ast_result_t scope_iftype(pass_opt_t* opt, ast_t* ast)
while(sub_child != NULL)
{
ast_t* typeparam = make_iftype_typeparam(opt, sub_child, super_child,
then_clause);
ast);
if(typeparam == NULL)
{
ast_free_unattached(typeparams);
return AST_ERROR;
}

if(!set_scope(opt, then_clause, ast_child(typeparam), typeparam, true))
if(!set_scope(opt, ast, ast_child(typeparam), typeparam, true))
{
ast_free_unattached(typeparams);
return AST_ERROR;
Expand Down
2 changes: 1 addition & 1 deletion src/libponyc/pass/sugar.c
Original file line number Diff line number Diff line change
Expand Up @@ -1267,7 +1267,7 @@ ast_result_t pass_sugar(ast_t** astp, pass_opt_t* options)
case TK_IF:
case TK_WHILE:
case TK_REPEAT: return sugar_else(ast, 2);
case TK_IFTYPE: return sugar_else(ast, 3);
case TK_IFTYPE_SET: return sugar_else(ast, 1);
case TK_TRY: return sugar_try(ast);
case TK_FOR: return sugar_for(options, astp);
case TK_WITH: return sugar_with(options, astp);
Expand Down
5 changes: 3 additions & 2 deletions src/libponyc/reach/reach.c
Original file line number Diff line number Diff line change
Expand Up @@ -1240,9 +1240,10 @@ static void reachable_expr(reach_t* r, ast_t* ast, pass_opt_t* opt)
break;
}

case TK_IFTYPE:
case TK_IFTYPE_SET:
{
AST_GET_CHILDREN(ast, sub, super, left, right);
AST_GET_CHILDREN(ast, left_clause, right);
AST_GET_CHILDREN(left_clause, sub, super, left);

ast_t* type = ast_type(ast);

Expand Down
28 changes: 11 additions & 17 deletions src/libponyc/type/reify.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,25 @@
#include "../ast/token.h"
#include "ponyassert.h"

static void reify_typeparamref(pass_opt_t* opt, ast_t** astp, ast_t* typeparam, ast_t* typearg)
static void reify_typeparamref(ast_t** astp, ast_t* typeparam, ast_t* typearg)
{
ast_t* ast = *astp;
pony_assert(ast_id(ast) == TK_TYPEPARAMREF);
pony_assert(ast_id(typeparam) == TK_TYPEPARAM);

ast_t* ref_def = (ast_t*)ast_data(ast);
ast_t* param_def = (ast_t*)ast_data(typeparam);

// We can't compare ref_def and typeparam, as they could be a copy or
// a iftype shadowing. However, their data points back to the original
// typeparam definition, which can be compared.
ref_def = (ast_t*)ast_data(ref_def);
typeparam = (ast_t*)ast_data(typeparam);

pony_assert(ref_def != NULL);
pony_assert(param_def != NULL);
AST_GET_CHILDREN(ref_def, ref_name, ref_constraint);
AST_GET_CHILDREN(param_def, param_name, param_constraint);
pony_assert(typeparam != NULL);

if(ref_def != param_def)
{
if(ast_name(ref_name) == ast_name(param_name))
{
if((ast_id(param_constraint) != TK_TYPEPARAMREF) &&
!is_subtype(ref_constraint, param_constraint, NULL, opt))
return;
} else {
return;
}
}
if(ref_def != typeparam)
return;

// Keep ephemerality.
switch(ast_id(ast_childidx(ast, 2)))
Expand Down Expand Up @@ -117,7 +111,7 @@ static void reify_one(pass_opt_t* opt, ast_t** astp, ast_t* typeparam, ast_t* ty
switch(ast_id(ast))
{
case TK_TYPEPARAMREF:
reify_typeparamref(opt, astp, typeparam, typearg);
reify_typeparamref(astp, typeparam, typearg);
break;

case TK_ARROW:
Expand Down
72 changes: 72 additions & 0 deletions test/libponyc/iftype.cc
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,75 @@ TEST_F(IftypeTest, UnconstrainedTypeparam)

TEST_COMPILE(src);
}

TEST_F(IftypeTest, RecursiveConstraint)
{
const char* src =
"trait T[X: T[X] #any]\n"
" fun tag m(): X\n"

"class val C is T[C]\n"
" fun tag m(): C => C.create()\n"

"actor Main\n"
" new create(env: Env) =>\n"
" foo[C](C)\n"

" fun foo[A](x': A) =>\n"
" var x = x'\n"
" iftype A <: T[A] then\n"
" x = x.m()\n"
" end";

TEST_COMPILE(src);
}


TEST_F(IftypeTest, Tuple_MutuallyRecursiveConstraint)
{
const char* src =
"trait T[X]\n"
" fun tag m(): X\n"

"class val C is T[D]\n"
" fun tag m(): D => D.create()\n"

"class val D is T[C]\n"
" fun tag m(): C => C.create()\n"

"actor Main\n"
" new create(env: Env) =>\n"
" foo[C,D](C, D)\n"

" fun foo[A, B](x': A, y': B) =>\n"
" var x = x'\n"
" var y = y'\n"
" iftype (A, B) <: (T[B], T[A]) then\n"
" x = y.m()\n"
" y = x.m()\n"
" end";

TEST_COMPILE(src);
}


TEST_F(IftypeTest, NestedCond)
{
const char* src =
"trait T\n"
"class C is T\n"
" fun tag c() => None\n"

"actor Main\n"
" new create(env: Env) =>\n"
" foo[C](C)\n"

" fun foo[A](x: A) =>\n"
" iftype A <: T then\n"
" iftype A <: C then\n"
" x.c()\n"
" end\n"
" end";

TEST_COMPILE(src);
}