Skip to content

Commit

Permalink
Allow tuples to match empty interfaces
Browse files Browse the repository at this point in the history
Closes #2028.
  • Loading branch information
Benoit Vey authored and SeanTAllen committed Feb 7, 2018
1 parent 00f6113 commit c560881
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 54 deletions.
3 changes: 3 additions & 0 deletions src/libponyc/codegen/genmatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,10 @@ static bool static_tuple(compile_t* c, LLVMValueRef value, ast_t* type,
{
case TK_UNIONTYPE:
case TK_ISECTTYPE:
case TK_NOMINAL:
{
pony_assert((ast_id(type) != TK_NOMINAL) || is_top_type(type, true));

// Read the dynamic type and get a base pointer.
LLVMValueRef desc = gendesc_fetch(c, value);
LLVMValueRef ptr = gendesc_ptr_to_fields(c, value, desc);
Expand Down
82 changes: 52 additions & 30 deletions src/libponyc/type/matchtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,28 @@ static matchtype_t is_tuple_match_tuple(ast_t* operand, ast_t* pattern,
return ok;
}

static matchtype_t is_nominal_match_tuple(ast_t* operand, ast_t* pattern,
pass_opt_t* opt)
{
if(!is_top_type(operand, true))
return MATCHTYPE_REJECT;

ast_t* child = ast_child(pattern);

while(child != NULL)
{
matchtype_t r = is_x_match_x(operand, child, opt);
pony_assert(r != MATCHTYPE_REJECT);

if(r == MATCHTYPE_DENY)
return r;

child = ast_sibling(child);
}

return MATCHTYPE_ACCEPT;
}

static matchtype_t is_typeparam_match_x(ast_t* operand, ast_t* pattern,
pass_opt_t* opt)
{
Expand All @@ -177,6 +199,34 @@ static matchtype_t is_typeparam_match_x(ast_t* operand, ast_t* pattern,
return ok;
}

static matchtype_t is_arrow_match_x(ast_t* operand, ast_t* pattern,
pass_opt_t* opt)
{
// upperbound(this->T1) match T2
// ---
// (this->T1) match T2

// lowerbound(T1->T2) match T3
// ---
// (T1->T2) match T3

ast_t* operand_view;

AST_GET_CHILDREN(operand, left, right);

if(ast_id(left) == TK_THISTYPE)
operand_view = viewpoint_upper(operand);
else
operand_view = viewpoint_lower(operand);

if(operand_view == NULL)
return MATCHTYPE_DENY;

matchtype_t ok = is_x_match_x(operand_view, pattern, opt);
ast_free_unattached(operand_view);
return ok;
}

static matchtype_t is_x_match_tuple(ast_t* operand, ast_t* pattern,
pass_opt_t* opt)
{
Expand All @@ -192,13 +242,13 @@ static matchtype_t is_x_match_tuple(ast_t* operand, ast_t* pattern,
return is_tuple_match_tuple(operand, pattern, opt);

case TK_NOMINAL:
return MATCHTYPE_REJECT;
return is_nominal_match_tuple(operand, pattern, opt);

case TK_TYPEPARAMREF:
return is_typeparam_match_x(operand, pattern, opt);

case TK_ARROW:
return MATCHTYPE_REJECT;
return is_arrow_match_x(operand, pattern, opt);

default: {}
}
Expand Down Expand Up @@ -343,34 +393,6 @@ static matchtype_t is_nominal_match_nominal(ast_t* operand, ast_t* pattern,
return MATCHTYPE_DENY;
}

static matchtype_t is_arrow_match_x(ast_t* operand, ast_t* pattern,
pass_opt_t* opt)
{
// upperbound(this->T1) match T2
// ---
// (this->T1) match T2

// lowerbound(T1->T2) match T3
// ---
// (T1->T2) match T3

ast_t* operand_view;

AST_GET_CHILDREN(operand, left, right);

if(ast_id(left) == TK_THISTYPE)
operand_view = viewpoint_upper(operand);
else
operand_view = viewpoint_lower(operand);

if(operand_view == NULL)
return MATCHTYPE_DENY;

matchtype_t ok = is_x_match_x(operand_view, pattern, opt);
ast_free_unattached(operand_view);
return ok;
}

static matchtype_t is_x_match_nominal(ast_t* operand, ast_t* pattern,
pass_opt_t* opt)
{
Expand Down
111 changes: 88 additions & 23 deletions src/libponyc/type/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -708,40 +708,32 @@ static bool is_single_sub_tuple(ast_t* sub, ast_t* super, check_cap_t check_cap,
static bool is_tuple_sub_nominal(ast_t* sub, ast_t* super,
check_cap_t check_cap, errorframe_t* errorf, pass_opt_t* opt)
{
ast_t* super_def = (ast_t*)ast_data(super);

if(ast_id(super_def) == TK_INTERFACE)
if(is_top_type(super, true))
{
ast_t* super_members = ast_childidx(super_def, 4);
pony_assert(ast_id(super_members) == TK_MEMBERS);

if(ast_childcount(super_members) == 0)
for(ast_t* child = ast_child(sub);
child != NULL;
child = ast_sibling(child))
{
// This is an empty interface, so we let the tuple be a subtype if the
// caps of the elements of the tuple are subcaps of the interface cap.
for(ast_t* child = ast_child(sub);
child != NULL;
child = ast_sibling(child))
if(!is_x_sub_x(child, super, check_cap, errorf, opt))
{
if(!is_x_sub_x(child, super, check_cap, errorf, opt))
if(errorf != NULL)
{
if(errorf != NULL)
{
ast_error_frame(errorf, child,
"%s is not a subtype of %s: %s is not a subtype of %s",
ast_print_type(sub), ast_print_type(super),
ast_print_type(child), ast_print_type(super));
}
return false;
ast_error_frame(errorf, child,
"%s is not a subtype of %s: %s is not a subtype of %s",
ast_print_type(sub), ast_print_type(super),
ast_print_type(child), ast_print_type(super));
}
return false;
}

return true;
}

return true;
}

if(errorf != NULL)
{
ast_t* super_def = (ast_t*)ast_data(super);

ast_error_frame(errorf, sub,
"%s is not a subtype of %s: the subtype is a tuple",
ast_print_type(sub), ast_print_type(super));
Expand Down Expand Up @@ -2027,6 +2019,79 @@ bool is_bare(ast_t* type)
return false;
}

bool is_top_type(ast_t* type, bool ignore_cap)
{
if(type == NULL)
return false;

switch(ast_id(type))
{
case TK_UNIONTYPE:
case TK_ISECTTYPE:
{
ast_t* child = ast_child(type);

while(child != NULL)
{
if(!is_top_type(child, ignore_cap))
return false;

child = ast_sibling(child);
}

return true;
}

case TK_NOMINAL:
{
if(!ignore_cap && (ast_id(cap_fetch(type)) != TK_TAG))
return false;

// An empty interface is a top type.
ast_t* def = (ast_t*)ast_data(type);

if(ast_id(def) != TK_INTERFACE)
return false;

ast_t* members = ast_childidx(def, 4);

if(ast_childcount(members) != 0)
return false;

return true;
}

case TK_ARROW:
{
if(ignore_cap)
return is_top_type(ast_childidx(type, 1), true);

ast_t* type_lower = viewpoint_lower(type);

if(type_lower == NULL)
return false;

bool r = is_top_type(type_lower, false);

ast_free_unattached(type_lower);
return r;
}

case TK_TUPLETYPE:
case TK_TYPEPARAMREF:
case TK_FUNTYPE:
case TK_INFERTYPE:
case TK_ERRORTYPE:
case TK_DONTCARETYPE:
return false;

default : {}
}

pony_assert(0);
return false;
}

bool is_entity(ast_t* type, token_id entity)
{
if(type == NULL)
Expand Down
2 changes: 2 additions & 0 deletions src/libponyc/type/subtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ bool is_known(ast_t* type);

bool is_bare(ast_t* type);

bool is_top_type(ast_t* type, bool ignore_cap);

bool is_entity(ast_t* type, token_id entity);

bool contains_dontcare(ast_t* ast);
Expand Down
12 changes: 11 additions & 1 deletion test/libponyc/matchtype.cc
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ TEST_F(MatchTypeTest, BothCompound)
TEST_F(MatchTypeTest, Tuples)
{
const char* src =
"interface I1\n"

"interface I2\n"
" fun f()\n"

"trait T1\n"
" fun f()\n"

Expand All @@ -323,7 +328,7 @@ TEST_F(MatchTypeTest, Tuples)
"interface Test\n"
" fun z(c1: C1, c2: C2, c3: C3, t1: T1, t2: T2,\n"
" c1c1: (C1, C1), c1c2: (C1, C2), c1c3: (C1, C3),\n"
" t1t2: (T1, T2))";
" t1t2: (T1, T2), i1: I1, i2: I2)";

TEST_COMPILE(src);

Expand All @@ -340,6 +345,11 @@ TEST_F(MatchTypeTest, Tuples)
ASSERT_EQ(
MATCHTYPE_ACCEPT, is_matchtype(type_of("c1c3"), type_of("t1t2"), &opt));

ASSERT_EQ(
MATCHTYPE_ACCEPT, is_matchtype(type_of("i1"), type_of("c1c1"), &opt));
ASSERT_EQ(
MATCHTYPE_REJECT, is_matchtype(type_of("i2"), type_of("c1c1"), &opt));

// We can't make types with don't cares in as the type of a parameter. Modify
// t1t2 instead.
ast_t* t1t2 = type_of("t1t2");
Expand Down

0 comments on commit c560881

Please sign in to comment.