diff --git a/packages/builtin/any.pony b/packages/builtin/any.pony index cf5a7394f..39b2f6d43 100644 --- a/packages/builtin/any.pony +++ b/packages/builtin/any.pony @@ -1,3 +1 @@ -interface tag Any - -interface tag AnyNoCheck \ No newline at end of file +interface tag Any \ No newline at end of file diff --git a/packages/builtin/array.pony b/packages/builtin/array.pony index 41d860f07..67d2d82f0 100644 --- a/packages/builtin/array.pony +++ b/packages/builtin/array.pony @@ -1,4 +1,4 @@ -class Array[A : AnyNoCheck] is Seq[A] +class Array[\allowstruct\ A] is Seq[A] """ Contiguous, resizable memory to store elements of type A. diff --git a/packages/builtin/c_fixed_sized_array.pony b/packages/builtin/c_fixed_sized_array.pony index 166e8a70b..e26418985 100644 --- a/packages/builtin/c_fixed_sized_array.pony +++ b/packages/builtin/c_fixed_sized_array.pony @@ -1,4 +1,4 @@ -struct CFixedSizedArray[A: AnyNoCheck, _size: USize] +struct CFixedSizedArray[\allowstruct\ A, _size: USize] """ Contiguous, fixed sized memory to store elements of type A. Useful for FFI interfaces and does not contain any extra @@ -211,7 +211,7 @@ fun pairs(): CFixedSizedArrayPairs[A, _size, this->CFixedSizedArray[A, _size]]^ CFixedSizedArrayPairs[A, _size, this->CFixedSizedArray[A, _size]](this) -class CFixedSizedArrayKeys[A, _size: USize, B: CFixedSizedArray[A, _size] #read] is Iterator[USize] +class CFixedSizedArrayKeys[A, _size: USize, \allowstruct\ B: CFixedSizedArray[A, _size] #read] is Iterator[USize] let _array: B var _i: USize @@ -230,7 +230,7 @@ class CFixedSizedArrayKeys[A, _size: USize, B: CFixedSizedArray[A, _size] #read] end -class CFixedSizedArrayValues[A, _size: USize, B: CFixedSizedArray[A, _size] #read] is Iterator[B->A] +class CFixedSizedArrayValues[A, _size: USize, \allowstruct\ B: CFixedSizedArray[A, _size] #read] is Iterator[B->A] let _array: B var _i: USize @@ -245,7 +245,7 @@ class CFixedSizedArrayValues[A, _size: USize, B: CFixedSizedArray[A, _size] #rea _array(_i = _i + 1)? -class CFixedSizedArrayPairs[A, _size: USize, B: CFixedSizedArray[A, _size] #read] is Iterator[(USize, B->A)] +class CFixedSizedArrayPairs[A, _size: USize, \allowstruct\ B: CFixedSizedArray[A, _size] #read] is Iterator[(USize, B->A)] let _array: B var _i: USize diff --git a/packages/builtin/fixed_sized_array.pony b/packages/builtin/fixed_sized_array.pony index 6bc49fc44..2a4a370d2 100644 --- a/packages/builtin/fixed_sized_array.pony +++ b/packages/builtin/fixed_sized_array.pony @@ -1,4 +1,4 @@ -class FixedSizedArray[A: AnyNoCheck, _size: USize] +class FixedSizedArray[\allowstruct\ A, _size: USize] """ Contiguous, fixed sized memory to store elements of type A. """ diff --git a/packages/builtin/nullable_pointer.pony b/packages/builtin/nullable_pointer.pony index 6de2c0b84..f3bfdc681 100644 --- a/packages/builtin/nullable_pointer.pony +++ b/packages/builtin/nullable_pointer.pony @@ -1,4 +1,4 @@ -struct NullablePointer[A: AnyNoCheck] +struct NullablePointer[\allowstruct\ A] """ A NullablePointer[A] is used to encode a possibly-null type. It should _only_ be used for structs that need to be passed to and from the C FFI. diff --git a/packages/builtin/pointer.pony b/packages/builtin/pointer.pony index 4e65965ac..27aafe359 100644 --- a/packages/builtin/pointer.pony +++ b/packages/builtin/pointer.pony @@ -1,4 +1,4 @@ -struct Pointer[A: AnyNoCheck] +struct Pointer[\allowstruct\ A] """ A Pointer[A] is a raw memory pointer. It has no descriptor and thus can't be included in a union or intersection, or be a subtype of any interface. Most @@ -22,7 +22,7 @@ struct Pointer[A: AnyNoCheck] """ compile_intrinsic - new from_any[B: AnyNoCheck](from: B) => + new from_any[\allowstruct\ B](from: B) => """ Initializes from any type to a Pointer. """ @@ -46,7 +46,7 @@ struct Pointer[A: AnyNoCheck] """ compile_intrinsic - fun convert[B: AnyNoCheck](): this->Pointer[B] => + fun convert[\allowstruct\ B](): this->Pointer[B] => """ Convert from Pointer[A] to Pointer[B]. """ diff --git a/src/libponyc/ast/parser.c b/src/libponyc/ast/parser.c index f9c40e3d3..af176be05 100644 --- a/src/libponyc/ast/parser.c +++ b/src/libponyc/ast/parser.c @@ -128,6 +128,7 @@ DEF(typearg); // ID [COLON type] [ASSIGN typearg] DEF(typeparam); AST_NODE(TK_TYPEPARAM); + ANNOTATE(annotations); TOKEN("name", TK_ID); IF(TK_COLON, RULE("type constraint", type)); IF(TK_ASSIGN, RULE("default type argument", typearg)); diff --git a/src/libponyc/ast/treecheckdef.h b/src/libponyc/ast/treecheckdef.h index 8d630e4bc..28430a84b 100644 --- a/src/libponyc/ast/treecheckdef.h +++ b/src/libponyc/ast/treecheckdef.h @@ -111,6 +111,7 @@ GROUP(value_formal_literal, int_literal, float_literal, bool_literal, string); RULE(value_formal_arg, + HAS_DATA CHILD(value_formal_literal, seq, comptime_expr) HAS_TYPE(type), TK_VALUEFORMALARG); diff --git a/src/libponyc/expr/call.c b/src/libponyc/expr/call.c index 85a4c9d81..12391bcc8 100644 --- a/src/libponyc/expr/call.c +++ b/src/libponyc/expr/call.c @@ -721,6 +721,16 @@ static bool method_call(pass_opt_t* opt, ast_t* ast) return false; AST_GET_CHILDREN(type, cap, typeparams, params, result); + + // This is needed because the return type might be unprocessed + // because the method appears later on in the AST. + // We must run the expression pass here because the return type + // is being set here and used for inferring the assigned type + if(ast_visit(&result, NULL, pass_expr, opt, PASS_EXPR) != AST_OK) + { + return false; + } + ast_settype(ast, result); return true; diff --git a/src/libponyc/expr/literal.c b/src/libponyc/expr/literal.c index 484bcfc90..f007832c9 100644 --- a/src/libponyc/expr/literal.c +++ b/src/libponyc/expr/literal.c @@ -841,6 +841,17 @@ static bool coerce_literal_to_type(ast_t** astp, ast_t* target_type, break; } + case TK_VALUEFORMALARG: + { + ast_t* l = ast_child(literal_expr); + if(!coerce_literal_to_type(&l, target_type, chain, opt, + report_errors)) + return false; + + ast_settype(literal_expr, ast_type(l)); + break; + } + default: ast_error(opt->check.errors, literal_expr, "Internal error, coerce_literal_to_type node %s", ast_get_print(literal_expr)); diff --git a/src/libponyc/expr/reference.c b/src/libponyc/expr/reference.c index 2e300e505..f7d2524ed 100644 --- a/src/libponyc/expr/reference.c +++ b/src/libponyc/expr/reference.c @@ -354,7 +354,7 @@ bool expr_typeref(pass_opt_t* opt, ast_t** astp) // This is needed in order to evaluate the value type arguments and give them a type // for type references. If Array or Pointer, skip the check. - if (ast_id(typeargs) != TK_NONE) + if(ast_id(typeargs) != TK_NONE) { ast_t* underlying_type = (ast_t*)ast_data(ast); if((underlying_type != NULL) && diff --git a/src/libponyc/type/reify.c b/src/libponyc/type/reify.c index 8fbb29b68..2e6329f05 100644 --- a/src/libponyc/type/reify.c +++ b/src/libponyc/type/reify.c @@ -209,8 +209,34 @@ static void reify_valueformalparamref(pass_opt_t* opt, ast_t** astp, ast_t* type if(ast_id(typearg) == TK_VALUEFORMALARG) { - ast_t* type = ast_childidx(found_typeparam, 1); - ast_settype(typearg, type); + ast_t* lit = ast_child(typearg); + ast_t* typeparam_type = ast_childidx(found_typeparam, 1); + + ast_t* literal_type = ast_type(lit); + if(literal_type == NULL) + { + // Why do we need to run the pass_expr and coerce_literals here? + // When reifying and replacing the TK_VALUEFORMALPARAMREF with + // a TK_VALUEFORMALARG the literals might not have been processed + // yet becuase it originates from something later on in the AST tree + + // Here we make literals out of the expression + if(ast_visit(&lit, NULL, pass_expr, opt, PASS_EXPR) != AST_OK) + { + pony_assert(false); + return; + } + + // Let's coerce the literal + if(!coerce_literals(&lit, typeparam_type, opt)) + { + pony_assert(false); + return; + } + } + + ast_setdata(typearg, found_typeparam); + ast_settype(typearg, typeparam_type); } ast_replace(astp, typearg); } @@ -314,7 +340,10 @@ bool reify_defaults(ast_t* typeparams, ast_t* typeargs, bool errors, opt->program_pass == PASS_EXPR) { ast_t* lit_child = ast_child(defarg); - pass_expr(&lit_child, opt); + if(ast_visit(&lit_child, NULL, pass_expr, opt, PASS_EXPR) != AST_OK) + { + return false; + } } ast_append(typeargs, defarg); @@ -564,31 +593,6 @@ bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs, while(typeparam != NULL) { - // Check if the constraint is name "AnyNoCheck" which will - // skip any checks for the type parameter. This is used by - // the builtin types like Pointer and Array among others. - ast_t* constraint_id = NULL; - ast_t* ref_or_type = ast_childidx(typeparam, 1); - if(ast_id(ref_or_type) == TK_NOMINAL) - { - constraint_id = ast_childidx(ref_or_type, 1); - } - else if(ast_id(ref_or_type) == TK_TYPEPARAMREF) - { - ast_t* constraint = typeparam_constraint(ref_or_type); - if(constraint != NULL && ast_id(constraint) == TK_NOMINAL) - { - constraint_id = ast_childidx(constraint, 1); - } - } - - if (constraint_id != NULL && strcmp(ast_name(constraint_id), "AnyNoCheck") == 0) - { - typeparam = ast_sibling(typeparam); - typearg = ast_sibling(typearg); - continue; - } - if (is_bare(typearg)) { if(report_errors) @@ -606,7 +610,7 @@ bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs, { ast_t* def = (ast_t*)ast_data(typearg); - if(ast_id(def) == TK_STRUCT) + if(ast_id(def) == TK_STRUCT && !ast_has_annotation(typeparam, "allowstruct")) { if(report_errors) { @@ -691,6 +695,7 @@ bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs, if(ast_id(typearg) == TK_VALUEFORMALARG) { ast_t* literal = ast_child(typearg); + if (!coerce_literals(&literal, r_constraint, opt)) return false; diff --git a/src/libponyc/type/subtype.c b/src/libponyc/type/subtype.c index 377aa6515..216c0f1e3 100644 --- a/src/libponyc/type/subtype.c +++ b/src/libponyc/type/subtype.c @@ -256,6 +256,36 @@ static bool is_literal_equal(ast_t* a, ast_t* b) return false; } +static ast_t* get_valueformalarg_type(ast_t* ast) +{ + ast_t* parent = ast_parent(ast); + ast_t* parent2 = ast_parent(parent); + + ast_t* def = NULL; + + switch(ast_id(parent2)) + { + case TK_NOMINAL: + case TK_TYPEREF: + def = (ast_t*)ast_data(parent2); + break; + + default: + pony_assert(false); + break; + } + + size_t index = ast_index(ast); + + ast_t* type_params = ast_childidx(def, 1); + + ast_t* typeparam = ast_childidx(type_params, index); + + ast_t* arg_type = ast_childidx(typeparam, 1); + + return arg_type; +} + static bool is_eq_typeargs(ast_t* a, ast_t* b, errorframe_t* errorf, pass_opt_t* opt) { @@ -282,7 +312,7 @@ static bool is_eq_typeargs(ast_t* a, ast_t* b, errorframe_t* errorf, if (!is_literal_equal(lit_a, lit_b)) ret = false; - if (!is_eqtype(ast_type(lit_a), ast_type(lit_b), errorf, opt)) + if (!is_eqtype(get_valueformalarg_type(a_arg), get_valueformalarg_type(b_arg), errorf, opt)) ret = false; } else diff --git a/test/full-program-tests/iftype-else-if-true/main.pony b/test/full-program-tests/iftype-else-if-true/main.pony index 4037426ae..121c17a12 100644 --- a/test/full-program-tests/iftype-else-if-true/main.pony +++ b/test/full-program-tests/iftype-else-if-true/main.pony @@ -70,28 +70,28 @@ actor Main false - fun is_class[A: AnyNoCheck](): Bool => + fun is_class[\allowstruct\ A](): Bool => iftype A <: class then true else false end - fun is_struct[A: AnyNoCheck](): Bool => + fun is_struct[\allowstruct\ A](): Bool => iftype A <: struct then true else false end - fun is_primitive[A: AnyNoCheck](): Bool => + fun is_primitive[\allowstruct\ A](): Bool => iftype A <: primitive then true else false end - fun is_class_or_primitive[A: AnyNoCheck](): Bool => + fun is_class_or_primitive[\allowstruct\ A](): Bool => iftype A <: (class | struct) then true else diff --git a/test/libponyc/iftype.cc b/test/libponyc/iftype.cc index 044faedf2..0a93811d0 100644 --- a/test/libponyc/iftype.cc +++ b/test/libponyc/iftype.cc @@ -326,7 +326,7 @@ TEST_F(IftypeTest, TestUnderlyingType) " foo[S1]()\n" " foo[P1]()\n" - " fun foo[A: AnyNoCheck]() =>\n" + " fun foo[\\allowstruct\\ A]() =>\n" " iftype A <: class then\n" " None\n" " end\n" diff --git a/test/libponyc/util.cc b/test/libponyc/util.cc index 4e5b9d26f..e78b440a5 100644 --- a/test/libponyc/util.cc +++ b/test/libponyc/util.cc @@ -84,13 +84,12 @@ static const char* const _builtin = " => None\n" "primitive None\n" "interface tag Any\n" - "interface tag AnyNoCheck\n" "primitive Bool\n" " new create(a: Bool) => a\n" " fun op_and(a: Bool): Bool => this and a\n" " fun op_not(): Bool => not this\n" "class val String\n" - "struct Pointer[A: AnyNoCheck]\n" + "struct Pointer[\\allowstruct\\ A]\n" " new create() => compile_intrinsic\n" " fun tag is_null(): Bool => compile_intrinsic\n" "interface Seq[A]\n" @@ -103,7 +102,7 @@ static const char* const _builtin = "class ArrayValues[A]\n" " fun ref has_next(): Bool => false\n" " fun ref next(): A ? => error\n" - "class Array[A: AnyNoCheck] is Seq[A]\n" + "class Array[\\allowstruct\\ A] is Seq[A]\n" " var _size: USize = 0\n" " var _alloc: USize = 0\n" " var _ptr: Pointer[A] = Pointer[A]\n" @@ -117,16 +116,9 @@ static const char* const _builtin = " fun ref next(): A ?\n" "primitive DoNotOptimise\n" " fun apply[A](obj: A) => compile_intrinsic\n" - "struct NullablePointer[A]\n" + "struct NullablePointer[\\allowstruct\\ A]\n" " new create(that: A) => compile_intrinsic\n" - "struct RuntimeOptions\n" - "struct box Optional[A: AnyNoCheck]" - " new none() => compile_intrinsic\n" - " new create(init: A) => compile_intrinsic\n" - " fun is_none() : Bool => compile_intrinsic\n" - " fun is_some(): Bool => compile_intrinsic\n" - " fun apply(): A? => error\n" - " fun get_no_check(): A => compile_intrinsic\n"; + "struct RuntimeOptions\n"; void Main_runtime_override_defaults_oo(void* opt) diff --git a/test/libponyc/value_dependent_types.cc b/test/libponyc/value_dependent_types.cc index ada69d7a9..db9aa5a61 100644 --- a/test/libponyc/value_dependent_types.cc +++ b/test/libponyc/value_dependent_types.cc @@ -6,6 +6,9 @@ #define TEST_COMPILE(src) DO(test_compile(src, "expr")) #define TEST_ERROR(src) DO(test_error(src, "expr")) +#define TEST_COMPILE_REACH(src) DO(test_compile(src, "reach")) +#define TEST_ERROR_REACH(src) DO(test_error(src, "reach")) + class VDTTest: public PassTest {}; @@ -590,3 +593,31 @@ TEST_F(VDTTest, DISABLED_VDTTypeWithCompileTimeConstantError) TEST_ERROR(src); } +TEST_F(VDTTest, VDTTypeUseReturnTypeLaterInAst) +{ + const char* src = + "actor Main\n" + " new create(env: Env) =>\n" + " let a1: C[U8, 4] = C[U8, 4]\n" + " let a2: C[U8, 4] = C[U8, 4]\n" + " let a3 = foo(a1, a2)\n" + " for v in a3.values() do\n" + " v\n" + " end\n" + " fun foo(x: C[U8, 4], y: C[U8, 4]): C[U8, 4] =>\n" + " x\n" + "struct C[\\allowstruct\\ A, _size: USize]\n" + " fun values(): C2[A, _size, this->C[A, _size]]^ =>\n" + " C2[A, _size, this->C[A, _size]](this)\n" + "struct C2[A, _size: USize, \\allowstruct\\ B : C[A, _size] #read] is Iterator[B]\n" + " var _a: B" + " new create(a': B) =>\n" + " _a = a'\n" + " fun has_next(): Bool =>\n" + " true\n" + " fun ref next(): B =>\n" + " _a\n"; + + TEST_COMPILE_REACH(src); +} +