Skip to content

Commit

Permalink
Add iftype conditions (RFC 26) (#1855)
Browse files Browse the repository at this point in the history
These new conditions allow programmers to get information on subtyping
relationships of type parameters at compile time to conditionally
compile code.

Most of the work is done during the scope pass, where the definition
assigned to a type parameter reference differs based on whether the
reference occurs in the then clause of an iftype condition.

This change implements only a part of RFC 26. Method specialisation is
still to be implemented.
  • Loading branch information
Benoit Vey authored and jemc committed Apr 21, 2017
1 parent e84506d commit 8171738
Show file tree
Hide file tree
Showing 26 changed files with 696 additions and 32 deletions.
6 changes: 6 additions & 0 deletions pony.g
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ binop
nextterm
: 'if' ('\\' ID (',' ID)* '\\')? rawseq 'then' rawseq (elseif | ('else' annotatedrawseq))? 'end'
| 'ifdef' ('\\' ID (',' ID)* '\\')? infix 'then' rawseq (elseifdef | ('else' annotatedrawseq))? 'end'
| 'iftype' ('\\' ID (',' ID)* '\\')? type '<:' type 'then' rawseq (elseiftype | ('else' annotatedrawseq))? 'end'
| 'match' ('\\' ID (',' ID)* '\\')? rawseq caseexpr* ('else' annotatedrawseq)? 'end'
| 'while' ('\\' ID (',' ID)* '\\')? rawseq 'do' rawseq ('else' annotatedrawseq)? 'end'
| 'repeat' ('\\' ID (',' ID)* '\\')? rawseq 'until' annotatedrawseq ('else' annotatedrawseq)? 'end'
Expand All @@ -105,6 +106,7 @@ nextterm
term
: 'if' ('\\' ID (',' ID)* '\\')? rawseq 'then' rawseq (elseif | ('else' annotatedrawseq))? 'end'
| 'ifdef' ('\\' ID (',' ID)* '\\')? infix 'then' rawseq (elseifdef | ('else' annotatedrawseq))? 'end'
| 'iftype' ('\\' ID (',' ID)* '\\')? type '<:' type 'then' rawseq (elseiftype | ('else' annotatedrawseq))? 'end'
| 'match' ('\\' ID (',' ID)* '\\')? rawseq caseexpr* ('else' annotatedrawseq)? 'end'
| 'while' ('\\' ID (',' ID)* '\\')? rawseq 'do' rawseq ('else' annotatedrawseq)? 'end'
| 'repeat' ('\\' ID (',' ID)* '\\')? rawseq 'until' annotatedrawseq ('else' annotatedrawseq)? 'end'
Expand All @@ -125,6 +127,10 @@ caseexpr
: '|' ('\\' ID (',' ID)* '\\')? pattern? ('if' rawseq)? ('=>' rawseq)?
;

elseiftype
: 'elseiftype' ('\\' ID (',' ID)* '\\')? type '<:' type 'then' rawseq (elseiftype | ('else' annotatedrawseq))?
;

elseifdef
: 'elseif' ('\\' ID (',' ID)* '\\')? infix 'then' rawseq (elseifdef | ('else' annotatedrawseq))?
;
Expand Down
17 changes: 17 additions & 0 deletions src/libponyc/ast/frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ bool frame_push(typecheck_t* t, ast_t* ast)
{
ast_t* parent = ast_parent(ast);

if(parent == NULL)
return pop;

switch(ast_id(parent))
{
case TK_TYPEPARAM:
Expand Down Expand Up @@ -259,6 +262,20 @@ bool frame_push(typecheck_t* t, ast_t* ast)
break;
}

case TK_IFTYPE:
{
AST_GET_CHILDREN(parent, l_type, r_type, body);

pop = push_frame(t);
if(r_type == ast)
{
t->frame->iftype_constraint = ast;
} else if(body == ast) {
t->frame->iftype_body = ast;
}
break;
}

default: {}
}
break;
Expand Down
2 changes: 2 additions & 0 deletions src/libponyc/ast/frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ typedef struct typecheck_frame_t
ast_t* recover;
ast_t* ifdef_cond;
ast_t* ifdef_clause;
ast_t* iftype_constraint;
ast_t* iftype_body;

struct typecheck_frame_t* prev;
} typecheck_frame_t;
Expand Down
4 changes: 4 additions & 0 deletions src/libponyc/ast/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ static const lextoken_t symbols[] =

{ ".>", TK_CHAIN },

{ "<:", TK_SUBTYPE },

{ "\\", TK_BACKSLASH },

{ "{", TK_LBRACE },
Expand Down Expand Up @@ -165,9 +167,11 @@ static const lextoken_t keywords[] =

{ "if", TK_IF },
{ "ifdef", TK_IFDEF },
{ "iftype", TK_IFTYPE },
{ "then", TK_THEN },
{ "else", TK_ELSE },
{ "elseif", TK_ELSEIF },
{ "elseiftype", TK_ELSEIFTYPE },
{ "end", TK_END },
{ "for", TK_FOR },
{ "in", TK_IN },
Expand Down
37 changes: 33 additions & 4 deletions src/libponyc/ast/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,35 @@ DEF(ifdef);
REORDER(0, 2, 3, 1);
DONE();

// ELSEIFTYPE [annotations] type <: type THEN seq [elseiftype | (ELSE seq)]
DEF(elseiftype);
AST_NODE(TK_IFTYPE);
SCOPE();
SKIP(NULL, TK_ELSEIFTYPE);
ANNOTATE(annotations);
RULE("type", type);
SKIP(NULL, TK_SUBTYPE);
RULE("type", type);
SKIP(NULL, TK_THEN);
RULE("then value", seq);
OPT RULE("else clause", elseiftype, elseclause);
DONE();

// IFTYPE [annotations] type <: type THEN seq [elseiftype | (ELSE seq)] END
DEF(iftype);
PRINT_INLINE();
TOKEN(NULL, TK_IFTYPE);
ANNOTATE(annotations);
SCOPE();
RULE("type", type);
SKIP(NULL, TK_SUBTYPE);
RULE("type", type);
SKIP(NULL, TK_THEN);
RULE("then value", seq);
OPT RULE("else clause", elseiftype, elseclause);
TERMINATE("iftype expression", TK_END);
DONE();

// PIPE [annotations] [infix] [WHERE rawseq] [ARROW rawseq]
DEF(caseexpr);
AST_NODE(TK_CASE);
Expand Down Expand Up @@ -876,18 +905,18 @@ DEF(test_ifdef_flag);
TOKEN(NULL, TK_ID);
DONE();

// cond | ifdef | match | whileloop | repeat | forloop | with | try |
// cond | ifdef | iftype | match | whileloop | repeat | forloop | with | try |
// recover | consume | pattern | const_expr | test_<various>
DEF(term);
RULE("value", cond, ifdef, match, whileloop, repeat, forloop, with,
RULE("value", cond, ifdef, iftype, 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 | match | whileloop | repeat | forloop | with | try |
// cond | ifdef | iftype | match | whileloop | repeat | forloop | with | try |
// recover | consume | pattern | const_expr | test_<various>
DEF(nextterm);
RULE("value", cond, ifdef, match, whileloop, repeat, forloop, with,
RULE("value", cond, ifdef, iftype, 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
3 changes: 3 additions & 0 deletions src/libponyc/ast/token.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ typedef enum token_id
TK_ISECTTYPE,
TK_EPHEMERAL,
TK_ALIASED,
TK_SUBTYPE,

TK_QUESTION,
TK_UNARY_MINUS,
Expand Down Expand Up @@ -143,9 +144,11 @@ typedef enum token_id

TK_IF,
TK_IFDEF,
TK_IFTYPE,
TK_THEN,
TK_ELSE,
TK_ELSEIF,
TK_ELSEIFTYPE,
TK_END,
TK_WHILE,
TK_DO,
Expand Down
10 changes: 10 additions & 0 deletions src/libponyc/codegen/gencontrol.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ LLVMValueRef gen_if(compile_t* c, ast_t* ast)
return GEN_NOTNEEDED;
}

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

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

return gen_expr(c, right);
}

LLVMValueRef gen_while(compile_t* c, ast_t* ast)
{
bool needed = is_result_needed(ast);
Expand Down
2 changes: 2 additions & 0 deletions src/libponyc/codegen/gencontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ LLVMValueRef gen_seq(compile_t* c, ast_t* ast);

LLVMValueRef gen_if(compile_t* c, ast_t* ast);

LLVMValueRef gen_iftype(compile_t* c, ast_t* ast);

LLVMValueRef gen_while(compile_t* c, ast_t* ast);

LLVMValueRef gen_repeat(compile_t* c, ast_t* ast);
Expand Down
4 changes: 4 additions & 0 deletions src/libponyc/codegen/genexpr.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ LLVMValueRef gen_expr(compile_t* c, ast_t* ast)
ret = gen_if(c, ast);
break;

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

case TK_WHILE:
ret = gen_while(c, ast);
break;
Expand Down
39 changes: 39 additions & 0 deletions src/libponyc/expr/control.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "../type/assemble.h"
#include "../type/subtype.h"
#include "../type/alias.h"
#include "../type/typeparam.h"
#include "ponyassert.h"

bool expr_seq(pass_opt_t* opt, ast_t* ast)
Expand Down Expand Up @@ -133,6 +134,44 @@ bool expr_if(pass_opt_t* opt, ast_t* ast)
return true;
}

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);

ast_t* type = NULL;

if(!ast_checkflag(left, AST_FLAG_JUMPS_AWAY))
{
if(is_typecheck_error(ast_type(left)))
return false;

type = control_type_add_branch(opt, type, left);
}

if(!ast_checkflag(right, AST_FLAG_JUMPS_AWAY))
{
if(is_typecheck_error(ast_type(right)))
return false;

type = control_type_add_branch(opt, type, right);
}

if(type == NULL)
{
if((ast_id(ast_parent(ast)) == TK_SEQ) && ast_sibling(ast) != NULL)
{
ast_error(opt->check.errors, ast_sibling(ast), "unreachable code");
return false;
}
}

ast_settype(ast, type);
literal_unify_control(ast, opt);

return true;
}

bool expr_while(pass_opt_t* opt, ast_t* ast)
{
pony_assert(ast_id(ast) == TK_WHILE);
Expand Down
1 change: 1 addition & 0 deletions src/libponyc/expr/control.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ PONY_EXTERN_C_BEGIN

bool expr_seq(pass_opt_t* opt, ast_t* ast);
bool expr_if(pass_opt_t* opt, ast_t* ast);
bool expr_iftype(pass_opt_t* opt, ast_t* ast);
bool expr_while(pass_opt_t* opt, ast_t* ast);
bool expr_repeat(pass_opt_t* opt, ast_t* ast);
bool expr_try(pass_opt_t* opt, ast_t* ast);
Expand Down
7 changes: 7 additions & 0 deletions src/libponyc/expr/reference.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "../type/cap.h"
#include "../type/reify.h"
#include "../type/lookup.h"
#include "../type/typeparam.h"
#include "../ast/astbuild.h"
#include "ponyassert.h"

Expand Down Expand Up @@ -127,6 +128,8 @@ bool expr_fieldref(pass_opt_t* opt, ast_t* ast, ast_t* find, token_id tid)

AST_GET_CHILDREN(find, id, f_type, init);

f_type = typeparam_current(opt, f_type, ast);

// Viewpoint adapted type of the field.
ast_t* type = viewpoint_type(l_type, f_type);

Expand Down Expand Up @@ -368,6 +371,8 @@ bool expr_localref(pass_opt_t* opt, ast_t* ast)
if(is_typecheck_error(type))
return false;

type = typeparam_current(opt, type, ast);

if(!sendable(type))
{
if(opt->check.frame->recover != NULL)
Expand Down Expand Up @@ -425,6 +430,8 @@ bool expr_paramref(pass_opt_t* opt, ast_t* ast)
if(is_typecheck_error(type))
return false;

type = typeparam_current(opt, type, ast);

if(!sendable(type) && (opt->check.frame->recover != NULL))
{
ast_t* parent = ast_parent(ast);
Expand Down
14 changes: 14 additions & 0 deletions src/libponyc/pass/expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ 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.
if((ast_child(parent) == ast) || (ast_childidx(parent, 1) == ast))
return false;

return is_result_needed(parent);

case TK_REPEAT:
// Cond needed, body/else needed only if parent needed.
if(ast_childidx(parent, 1) == ast)
Expand Down Expand Up @@ -128,6 +135,12 @@ bool is_method_result(typecheck_t* t, ast_t* ast)
return false;
break;

case TK_IFTYPE:
// The subtype and the supertype are not the result.
if((ast_child(parent) == ast) || (ast_childidx(parent, 1) == ast))
return false;
break;

case TK_REPEAT:
// The condition is not the result.
if(ast_childidx(parent, 1) == ast)
Expand Down Expand Up @@ -241,6 +254,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_WHILE: r = expr_while(options, ast); break;
case TK_REPEAT: r = expr_repeat(options, ast); break;
case TK_TRY_NO_CHECK:
Expand Down
1 change: 1 addition & 0 deletions src/libponyc/pass/flatten.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ static ast_result_t flatten_isect(pass_opt_t* opt, ast_t* ast)
AST_EXTRACT_CHILDREN(ast, left, right);

if((opt->check.frame->constraint == NULL) &&
(opt->check.frame->iftype_constraint == NULL) &&
(opt->check.frame->provides == NULL) &&
!is_compat_type(left, right))
{
Expand Down
8 changes: 7 additions & 1 deletion src/libponyc/pass/names.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,18 @@ static bool names_typeparam(pass_opt_t* opt, ast_t** astp, ast_t* def)
}
}

const char* name = ast_name(id);

// Change to a typeparamref.
REPLACE(astp,
NODE(TK_TYPEPARAMREF,
TREE(id)
TREE(cap)
TREE(ephemeral)));

if(opt->check.frame->iftype_body != NULL)
def = ast_get(ast, name, NULL);

ast_setdata(*astp, def);
return true;
}
Expand All @@ -210,7 +215,8 @@ static bool names_type(pass_opt_t* opt, ast_t** astp, ast_t* def)

if(tcap == TK_NONE)
{
if(opt->check.frame->constraint != NULL)
if((opt->check.frame->constraint != NULL) ||
(opt->check.frame->iftype_constraint != NULL))
{
// A primitive constraint is a val, otherwise #any.
if(ast_id(def) == TK_PRIMITIVE)
Expand Down
Loading

0 comments on commit 8171738

Please sign in to comment.