diff --git a/CHANGELOG.md b/CHANGELOG.md index 31df04c5202..59851712fb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to the Pony compiler and standard library will be documented ### Fixed +- Fix unsoundness when replacing `this` viewpoint in method calls. ([PR #2503](https://github.com/ponylang/ponyc/pull/2503)) ### Added diff --git a/src/libponyc/type/lookup.c b/src/libponyc/type/lookup.c index b3188fc206d..c93319caa46 100644 --- a/src/libponyc/type/lookup.c +++ b/src/libponyc/type/lookup.c @@ -15,6 +15,44 @@ static deferred_reification_t* lookup_base(pass_opt_t* opt, ast_t* from, ast_t* orig, ast_t* type, const char* name, bool errors); +// If a box method is being called with an iso/trn receiver, we mustn't replace +// this-> by iso/trn as this would be unsound, but by ref. See #1887 +// +// This method (recursively) replaces occurences of iso and trn in `receiver` by +// ref. If a modification was required then a copy is returned, otherwise the +// original pointer is. +static ast_t* downcast_iso_trn_receiver_to_ref(ast_t* receiver) { + switch (ast_id(receiver)) + { + case TK_NOMINAL: + case TK_TYPEPARAMREF: + switch (cap_single(receiver)) + { + case TK_TRN: + case TK_ISO: + return set_cap_and_ephemeral(receiver, TK_REF, TK_NONE); + + default: + return receiver; + } + + case TK_ARROW: + { + AST_GET_CHILDREN(receiver, left, right); + + ast_t* downcasted_right = downcast_iso_trn_receiver_to_ref(right); + if(right != downcasted_right) + return viewpoint_type(left, downcasted_right); + else + return receiver; + } + + default: + pony_assert(0); + return NULL; + } +} + static deferred_reification_t* lookup_nominal(pass_opt_t* opt, ast_t* from, ast_t* orig, ast_t* type, const char* name, bool errors) { @@ -178,6 +216,10 @@ static deferred_reification_t* lookup_nominal(pass_opt_t* opt, ast_t* from, } ast_t* typeargs = ast_childidx(type, 2); + + if(ast_id(find) == TK_FUN && ast_id(ast_child(find)) == TK_BOX) + orig = downcast_iso_trn_receiver_to_ref(orig); + return deferred_reify_new(find, typeparams, typeargs, orig); } diff --git a/test/libponyc/badpony.cc b/test/libponyc/badpony.cc index 9c4f971d2f8..d8c50c0e55d 100644 --- a/test/libponyc/badpony.cc +++ b/test/libponyc/badpony.cc @@ -954,3 +954,20 @@ TEST_F(BadPonyTest, ApplySugarInferredLambdaArgument) TEST_COMPILE(src); } + +TEST_F(BadPonyTest, ThisViewpointWithIsoReceiver) +{ + // From issue #1887 + const char* src = + "class A\n" + "class Revealer\n" + " fun box reveal(x: this->A ref): A box => x\n" + + "actor Main\n" + "new create(env: Env) =>\n" + " let revealer : Revealer iso = Revealer.create()\n" + " let opaque : A tag = A.create()\n" + " let not_opaque : A box = (consume revealer).reveal(opaque)\n"; + + TEST_ERRORS_1(src, "argument not a subtype of parameter"); +}