From c560881c4eb956b3bfb631a781864816d5b79db0 Mon Sep 17 00:00:00 2001 From: Benoit Vey Date: Sat, 3 Feb 2018 20:42:49 +0100 Subject: [PATCH] Allow tuples to match empty interfaces Closes #2028. --- src/libponyc/codegen/genmatch.c | 3 + src/libponyc/type/matchtype.c | 82 ++++++++++++++--------- src/libponyc/type/subtype.c | 111 +++++++++++++++++++++++++------- src/libponyc/type/subtype.h | 2 + test/libponyc/matchtype.cc | 12 +++- 5 files changed, 156 insertions(+), 54 deletions(-) diff --git a/src/libponyc/codegen/genmatch.c b/src/libponyc/codegen/genmatch.c index 8938e4f71a..c20c55bbc0 100644 --- a/src/libponyc/codegen/genmatch.c +++ b/src/libponyc/codegen/genmatch.c @@ -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); diff --git a/src/libponyc/type/matchtype.c b/src/libponyc/type/matchtype.c index ac6077fa0a..b484e09f10 100644 --- a/src/libponyc/type/matchtype.c +++ b/src/libponyc/type/matchtype.c @@ -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) { @@ -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) { @@ -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: {} } @@ -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) { diff --git a/src/libponyc/type/subtype.c b/src/libponyc/type/subtype.c index 1c6f18fd83..8e905acb56 100644 --- a/src/libponyc/type/subtype.c +++ b/src/libponyc/type/subtype.c @@ -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)); @@ -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) diff --git a/src/libponyc/type/subtype.h b/src/libponyc/type/subtype.h index 91f06fd9da..927a3efddd 100644 --- a/src/libponyc/type/subtype.h +++ b/src/libponyc/type/subtype.h @@ -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); diff --git a/test/libponyc/matchtype.cc b/test/libponyc/matchtype.cc index 9505823d26..3955b886de 100644 --- a/test/libponyc/matchtype.cc +++ b/test/libponyc/matchtype.cc @@ -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" @@ -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); @@ -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");