Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should storing an array view in a variable make it non-view? #16275

Closed
mppf opened this issue Aug 21, 2020 · 17 comments · Fixed by #16180
Closed

Should storing an array view in a variable make it non-view? #16275

mppf opened this issue Aug 21, 2020 · 17 comments · Fixed by #16180

Comments

@mppf
Copy link
Member

mppf commented Aug 21, 2020

(Related to #5474 and #12178).

Generally, we have the behavior that storing a slice into a (non-ref) variable causes a copy of the slice to be made. Similarly for returning a slice from a function by value. (But we never created syntax for the alternative - see #12178).

In working on #16180, I was surprised that copying a reindex/rank change array left one with a reindex/rank change array. (However it does copy the elements). I was expecting that an array view would no longer be an array view once a copy of it is made. We've had this behavior since 1.18 (and the test program below didn't compile in 1.17 or 1.16).

Here is a test program to demonstrate:

proc returnIt(arg) { return arg; }

proc main() {
  var A: [1..2] real;
  var AA: [1..2, 1..2] real;

  {
    writeln();
    writeln("slice");
    const ref rB = A[1..1];
    writeln("to const ref is view:         ", rB.isSliceArrayView());

    var B = A[1..1];
    writeln("to var is view:               ", B.isSliceArrayView());
    
    const ref rReturned = returnIt(A[1..1]);
    writeln("returned is view:             ", rReturned.isSliceArrayView());

    const ref rD = A[1..1].domain;
    writeln(".domain to const ref is view: ", rD.isSliceDomainView());

    var D = A[1..1].domain;
    writeln(".domain to var is view:       ", D.isSliceDomainView());
  }

  {
    writeln();
    writeln("reindex");
    const ref rB = A.reindex(3..4);
    writeln("to const ref is view:         ", rB.isReindexArrayView());

    var B = A.reindex(3..4);
    writeln("to var is view:               ", B.isReindexArrayView());
    
    const ref rReturned = returnIt(A.reindex(3..4));
    writeln("returned is view:             ", rReturned.isReindexArrayView());

    const ref rD = A.reindex(3..4).domain;
    writeln(".domain to const ref is view: ", rD.isReindexDomainView());

    var D = A.reindex(3..4).domain;
    writeln(".domain to var is view:       ", D.isReindexDomainView());
  }

  {
    writeln();
    writeln("rank change");
    const ref rB = AA[1, ..];
    writeln("to const ref is view:         ", rB.isRankChangeArrayView());
    
    var B = AA[1, ..];
    writeln("to var is view:               ", B.isRankChangeArrayView());

    const ref rReturned = returnIt(AA[1, ..]);
    writeln("returned is view:             ", rReturned.isRankChangeArrayView());

    const ref rD = AA[1, ..].domain;
    writeln(".domain to const ref is view: ", rD.isRankChangeDomainView());

    var D = AA[1, ..].domain;
    writeln(".domain to var is view:       ", D.isRankChangeDomainView());
  }
}

It outputs


slice
to const ref is view:         true
to var is view:               false
returned is view:             false
.domain to const ref is view: false
.domain to var is view:       false

reindex
to const ref is view:         true
to var is view:               true
returned is view:             true
.domain to const ref is view: true
.domain to var is view:       true

rank change
to const ref is view:         true
to var is view:               true
returned is view:             true
.domain to const ref is view: true
.domain to var is view:       true

however I would expect only the "to const ref" rows to print out true.

@vasslitvinov
Copy link
Member

While we can define these any way we want, I would prefer consistency among these three.

mppf added a commit to mppf/chapel that referenced this issue Aug 21, 2020
So far, only improves the array parts.
@kateaditya646
Copy link

how to work on that bug guys its a very easy

@mppf
Copy link
Member Author

mppf commented Aug 24, 2020

@kateaditya646 - I don't think this issue is easy or a good starting issue. Please see https://chapel-lang.org/contributing.html for guidance on how to contribute to this project (if you haven't already looked at it).

mppf added a commit to mppf/chapel that referenced this issue Aug 24, 2020
So far, only improves the array parts.
mppf added a commit to mppf/chapel that referenced this issue Aug 24, 2020
For ./test/arrays/ferguson/views-and-copying.chpl
mppf added a commit to mppf/chapel that referenced this issue Aug 26, 2020
So far, only improves the array parts.
mppf added a commit to mppf/chapel that referenced this issue Aug 26, 2020
For ./test/arrays/ferguson/views-and-copying.chpl
mppf added a commit to mppf/chapel that referenced this issue Aug 26, 2020
One of the cases stopped printing out a rank change array
(due to the resolution of issue chapel-lang#16275).

See issue chapel-lang#16300 which asks questions about the current output.
mppf added a commit to mppf/chapel that referenced this issue Aug 31, 2020
So far, only improves the array parts.
mppf added a commit to mppf/chapel that referenced this issue Aug 31, 2020
For ./test/arrays/ferguson/views-and-copying.chpl
mppf added a commit to mppf/chapel that referenced this issue Aug 31, 2020
One of the cases stopped printing out a rank change array
(due to the resolution of issue chapel-lang#16275).

See issue chapel-lang#16300 which asks questions about the current output.
@bradcray
Copy link
Member

I agree that all of the array captures / returns should be copies and not views.

IIRC (and a quick check of the source suggests this), slices use the domain that was used to express the slice (and its distribution) to characterize the slice's domain, whereas rank-change slices and reindexings need to create a new domain (and distribution) in general; so it seems to me it is probably correct for them to be asymmetric in this regard. Specifically, note that the ArrayViewSlice.chpl file doesn't have any classes corresponding to distributions or domains, but the other to ArrayView files do. That's what makes me think that there's a natural/intentional asymmetry here. This suggests to me that perhaps isSliceDomainView() isn't interesting / shouldn't exist? (since it's not a question that ever has a true answer?). Or maybe I just don't understand what these isDomainView queries are meant to be doing... (I don't think they're user-facing, are they?)

@mppf
Copy link
Member Author

mppf commented Sep 1, 2020

This suggests to me that perhaps isSliceDomainView() isn't interesting / shouldn't exist? (since it's not a question that ever has a true answer?)

I agree about that.

(I don't think they're user-facing, are they?)

AFAIK they are not user facing. I was just using them as an easy way for the test to check if it was getting a view or non-view type

so it seems to me it is probably correct for them to be asymmetric in this regard.

I think you are talking about the .domain to const ref is view line? FWIW after PR #16180 this test output is:


slice
to const ref is view:         true
to var is view:               false
returned is view:             false
.domain to const ref is view: false
.domain to var is view:       false

reindex
to const ref is view:         true
to var is view:               false
returned is view:             false
.domain to const ref is view: true
.domain to var is view:       false

rank change
to const ref is view:         true
to var is view:               false
returned is view:             false
.domain to const ref is view: true
.domain to var is view:       false

which seems reasonable to me.

@mppf
Copy link
Member Author

mppf commented Sep 1, 2020

@bradcray - I am wondering what you would expect the domain type for a view-captured-into-an array to be.

proc returnIt(arg) { return arg; }

proc main() {
  var A: [1..2] real;
  var AA: [1..2, 1..2] real;

  {
    writeln();
    writeln("reindex");
    var B = A.reindex(3..4);
    writeln("to var is view:               ", B.isReindexArrayView());

    const ref rD = B.domain;
    writeln("var.domain is view:           ", rD.isReindexDomainView());
  }

  {
    writeln();
    writeln("rank change");
    var B = AA[1, ..];
    writeln("to var is view:               ", B.isRankChangeArrayView());

    const ref rD = B.domain;
    writeln("var.domain is view:           ", rD.isRankChangeDomainView());
  }
}

Based on your comments on #16298 and #16300 - I suspect that your answer might be "the rank change/reindex domain".

FWIW the above program has this behavior on the main branch:


reindex
to var is view:               true
var.domain is view:           true

rank change
to var is view:               true
var.domain is view:           true

and currently this behavior with PR #16180:


reindex
to var is view:               false
var.domain is view:           false

rank change
to var is view:               false
var.domain is view:           false

Is the expected behavior this:


reindex
to var is view:               false
var.domain is view:           true

rank change
to var is view:               false
var.domain is view:           true

?

But is this interpretation in conflict with

I agree that all of the array captures / returns should be copies and not views.

?

I guess where I am feeling confused is that preserving the rank change domain would imply that the array is also a rank change array (because, otherwise, default rectangular (or other) array implementations would need to support rank change domains, which defeats part of the point of array views). We could certainly make a copy of the elements either way, but making a copy and leaving the array as a rank change is the original question of this issue.

#16300 (comment) discusses another concern with letting var B = A.reindex(3..4); have reindexed array type.

@mppf
Copy link
Member Author

mppf commented Sep 1, 2020

Note that issue is specifically about the type of the array view arrays.

The element copies (or not) are occurring as expected:

proc returnIt(arg) { return arg; }

proc main() {
  var A: [1..2] real;
  var AA: [1..2, 1..2] real;

  {
    writeln();
    writeln("slice");
    A = 0;
    const ref rB = A[1..1];
    A = 1;
    writeln("to const ref behaves view:         ", A[1]);

    A = 0;
    var B = A[1..1];
    A = 1;
    writeln("to var behaves view:               ", B[1]);
    
    A = 0;
    const ref rReturned = returnIt(A[1..1]);
    A = 1;
    writeln("returned behaves view:             ", rReturned[1]);
  }

  {
    writeln();
    writeln("reindex");
    A = 0;
    const ref rB = A.reindex(3..4);
    A = 1;
    writeln("to const ref behaves view:         ", rB[3]);

    A = 0;
    var B = A.reindex(3..4);
    A = 1;
    writeln("to var behaves view:               ", B[3]);
    
    A = 0;
    const ref rReturned = returnIt(A.reindex(3..4));
    A = 1;
    writeln("returned behaves view:             ", rReturned[3]);
  }

  {
    writeln();
    writeln("rank change");
    AA = 0;
    const ref rB = AA[1, ..];
    AA = 1;
    writeln("to const ref behaves view:         ", rB[1]);
    
    AA = 0;
    var B = AA[1, ..];
    AA = 1;
    writeln("to var behaves view:               ", B[1]);

    AA = 0;
    const ref rReturned = returnIt(AA[1, ..]);
    AA = 1;
    writeln("returned behaves view:             ", rReturned[1]);
  }
}

prints this on master and with PR #16180:


slice
to const ref behaves view:         1.0
to var behaves view:               0.0
returned behaves view:             0.0

reindex
to const ref behaves view:         1.0
to var behaves view:               0.0
returned behaves view:             0.0

rank change
to const ref behaves view:         1.0
to var behaves view:               0.0
returned behaves view:             0.0

@mppf
Copy link
Member Author

mppf commented Sep 1, 2020

If var B = A.reindex(3..4); needs to have the reindexed domain, it could have a new array type e.g. ReindexedArrayCopy, which would have a reindexed domain but own its elements (i.e. not be a "view").

@bradcray
Copy link
Member

bradcray commented Sep 1, 2020

I think you are talking about the .domain to const ref is view line?

Yeah, sorry. I was not clear about that.

Regarding your question about the captured array not being a view, but its domain being a view, I see the conflict that you're pointing out (and suspect that this is at the heart of why we're not capturing reindex and rank-change slices on master today?). Let me stew on this a bit more; I'm worried any answer I give off the cuff will be incorrect / problematic in the distributed case.

@bradcray
Copy link
Member

bradcray commented Sep 1, 2020

OK, I thought about this a bit more over lunch, and am now trying to capture my thoughts crisply, typing them down as I do so... (it may sound like I'm lecturing, but I'm really just trying to get the facts straight for myself, so if I'm lecturing anyone, it's myself).

Given a block-distributed array:

var A = newBlockArr({1..10}, real);

running on a 2-locale system, we'd expect locale 0 to own A[1..5] and locale 1 to own A[6..10]. Now if we reindex this:

ref B = A.reindex({0..9});

then we'd expect locale 0 to own B[0..4] and locale 1 to own B[5..9]. Now if we capture B into a new variable C:

var C = B;

we'd expect C's index set to be 0..9 and to have the same distribution as B and reindexed-A, such that locale 0 owns C[0..4] and locale 1 owns C[5..9]. But for C to be a deep copy of B/A such that modifications to C or A weren't reflected in the other.

While for a block distribution, it's conceivable to think of creating a new block distribution and domain that describe C's mapping directly, in the general case, not all distributions can support arbitrary closed-form representations of rank-change slices or reindexings (e.g., a Cyclic 1D distribution targeting three locales would have a hard time supporting a slice of 1..n by 2 reindexed to be 1..n/2 since it wouldn't distribute the reindexed indices in the traditional dense round-robin way that Cyclic does). And even for Block, it simplifies the domain map's implementation not to have to create variations of itself. And, we'd generally like to having new expressions inherit/reuse existing domains rather than create new ones all the time, particularly for domains that are expensive to set up or store. All of this is why we went to the current array view strategy in which those two operations create their own special domains and distributions which refer back to the original domain(s) and distribution. And it's why C's domain would still consider itself to be a domain view (since it's the same domain as B / reindexed-A.

But C obviously isn't, or shouldn't be, an array view in the sense that it has its own storage and isn't referring to a different array. How does this work today, since you note a few comments above that it is? (checking...). From what I'm seeing, it looks like C is represented the same way as "reindexed A" was in the sense that ArrayViewReindexDom.dsiBuildArray() asks the underlying domain to build a new array and then refers to it indirectly, as the original reindexing would. The main difference is that it sets ownsArrInstance to true to get the memory management correct when the view is destroyed. This flag essentially is the difference between an array view referring to a pre-existing array and a new array created over an array view's domain.

What if, instead, dsiBuildArray() returned a new/distinct array type as you propose above, ReindexedArrayCopy? Would we be able to implement all of the dsi interface on that new array type?

Taking dsiAccess() as an example, if we wrote C[7], we'd need the ReindexedArrayCopy.dsiAccess() to use the reindexed domain/distribution in order to determine which locale owned element 7, much as ArrayViewReindexArr.dsiAccess() does today. Thinking about dsiBuildArray() as another example, ReindexedArrayCopy.dsiBuildArray() would essentially have to defer to the underlying domains and distributions to determine what each locale should do to allocate C in a way that's compatible with reindexed A. So that also feels like it would end up being quite similar to what ReindexedArrayCopy does today.

So, if they're so similar, what would be the motivation for ReindexedArrayCopy? It would give us a distinct type and permit us to distinguish between the original array view and a copy of such an array view at compile-time. But maybe we could do that by converting the ownsArrInstance bool from const to param in order to not have two array class implementations that (from what I'm guessing) would end up being fairly similar?

Or, could/should the existing calls to check whether or not something is an array view be made into non-param queries such that they can check ownsArrInstance as well? (I'm guessing that the places we want to know this aren't all amenable to being runtime checks, but am not sure).

So I think that's why we are where we are today. I'm pausing here for now to get your thoughts, @mppf.

@mppf
Copy link
Member Author

mppf commented Sep 2, 2020

@bradcray - thanks very much for talking yourself (and me) through all of that.

From what I'm seeing, it looks like C is represented the same way as "reindexed A" was in the sense that ArrayViewReindexDom.dsiBuildArray() asks the underlying domain to build a new array and then refers to it indirectly, as the original reindexing would.

Somehow I never really caught on to this part, but it's good to know what's going on.

But maybe we could do that by converting the ownsArrInstance bool from const to param in order to not have two array class implementations that (from what I'm guessing) would end up being fairly similar?

Yep, that makes sense to me too. The compiler would just need to know how to check the param field in the right places, which is slightly harder but not a big deal.

Or, could/should the existing calls to check whether or not something is an array view be made into non-param queries such that they can check ownsArrInstance as well? (I'm guessing that the places we want to know this aren't all amenable to being runtime checks, but am not sure).

We used to do this, but it basically adds a bunch of complexity to the compiler-modules interface and is the reason we had chpl__unalias. However over time chpl__unalias evolved into something that could be handled better at compile-time only (even while it was probably always adding extra copies for view copies). Then, for better or worse, in PR #16180 I have been trying to remove chpl__unalias.

@bradcray
Copy link
Member

bradcray commented Sep 2, 2020

The compiler would just need to know how to check the param field in the right places, which is slightly harder but not a big deal.

Assuming the compiler currently relies on the "is*View() routines" (does it?) I was thinking that we could change them to check the param field (or add new queries and use them instead). If the compiler is doing something more primitive like looking directly at the type, you're right of course.

@mppf
Copy link
Member Author

mppf commented Sep 2, 2020

Assuming the compiler currently relies on the "is*View() routines" (does it?)

No, but this isn't a big deal either way.

@mppf
Copy link
Member Author

mppf commented Sep 2, 2020

Anyway I think I will try the param strategy and see if that gets us where we need to be.

mppf added a commit to mppf/chapel that referenced this issue Sep 2, 2020
Per discussion in issue chapel-lang#16275

Also adds the test variants from chapel-lang#16275 to views-and-copying.chpl
@mppf
Copy link
Member Author

mppf commented Sep 2, 2020

@bradcray - what about this part?


    var D = A.reindex(3..4).domain;
    writeln(".domain to var is view:       ", D.isReindexDomainView());

I think that we want D to have the same distribution as the original (Supposing A was block distributed or something) and therefore D should be a reindex domain. Is that right?

@bradcray
Copy link
Member

bradcray commented Sep 2, 2020

Hmm, that's a good question. (thinking...). Yes, I believe so. D should store the indices {3..4} but using the distribution that governs A's original indices, so I think it would need to be a reindex domain to achieve that in the general case.

mppf added a commit to mppf/chapel that referenced this issue Sep 3, 2020
So far, only improves the array parts.
mppf added a commit to mppf/chapel that referenced this issue Sep 3, 2020
For ./test/arrays/ferguson/views-and-copying.chpl
mppf added a commit to mppf/chapel that referenced this issue Sep 3, 2020
One of the cases stopped printing out a rank change array
(due to the resolution of issue chapel-lang#16275).

See issue chapel-lang#16300 which asks questions about the current output.
mppf added a commit to mppf/chapel that referenced this issue Sep 3, 2020
Per discussion in issue chapel-lang#16275

Also adds the test variants from chapel-lang#16275 to views-and-copying.chpl
mppf added a commit to mppf/chapel that referenced this issue Sep 3, 2020
So far, only improves the array parts.
mppf added a commit to mppf/chapel that referenced this issue Sep 3, 2020
For ./test/arrays/ferguson/views-and-copying.chpl
mppf added a commit to mppf/chapel that referenced this issue Sep 3, 2020
One of the cases stopped printing out a rank change array
(due to the resolution of issue chapel-lang#16275).

See issue chapel-lang#16300 which asks questions about the current output.
mppf added a commit to mppf/chapel that referenced this issue Sep 3, 2020
Per discussion in issue chapel-lang#16275

Also adds the test variants from chapel-lang#16275 to views-and-copying.chpl
@mppf
Copy link
Member Author

mppf commented Sep 3, 2020

PR #16180 will address these and adds the test test/arrays/ferguson/views-and-copying.chpl based on the examples from this issue.

mppf added a commit to mppf/chapel that referenced this issue Sep 8, 2020
So far, only improves the array parts.
mppf added a commit to mppf/chapel that referenced this issue Sep 8, 2020
For ./test/arrays/ferguson/views-and-copying.chpl
mppf added a commit to mppf/chapel that referenced this issue Sep 8, 2020
One of the cases stopped printing out a rank change array
(due to the resolution of issue chapel-lang#16275).

See issue chapel-lang#16300 which asks questions about the current output.
mppf added a commit to mppf/chapel that referenced this issue Sep 8, 2020
Per discussion in issue chapel-lang#16275

Also adds the test variants from chapel-lang#16275 to views-and-copying.chpl
mppf added a commit that referenced this issue Sep 8, 2020
Adjust inout to be implemented with a single argument

Resolves #16290
Resolves #16185 
Resolves #16195 
Resolves #16148
Resolves #16275
Resolves #16301
Resolves #16298
Resolves #16300
Resolves #16007

This PR takes the following steps:
 * changes `inout` to use a single argument instead of two arguments
 * removes `chpl__unref` and instead the compiler treats types that have
   `chpl__initCopy` return a different type specially. Additionally, for
   domains, instead of relying on runtime information, use compiler
   analysis to identify functions that return unowned domains as with
   `A.domain`.
 * allows types that have `chpl__initCopy` to return a different type to
   coerce to that type. This allows iterators to pass to array arguments
   with `in` intent (requested in issue #16007).

Follow-up to PR #16143 which changed `inout` to be implemented with an
`in` argument and an `out` argument. In reviewing that PR, Vass pointed
out that there might be alternatives to the two-argument approach. Since
the copy-for-in and write-back-for-out are both now occurring at the call
site, it is possible for the function body to just accept a `ref`
argument for `inout`. The function will accept the result of the
copy-for-in and then modify it. The code at the call site will do the
write-back from that to the original variable. This PR makes that change.
The implementation involved adjusting wrappers.cpp and adding a map to
track the value copied from in the `in` intent part of `inout` for use in
the `out` intent part.

There is one inout behavior change:

``` chapel
proc out_inout(out a: R, inout b: R) {
  writeln("a is ", a);
  writeln("b is ", b);
  a = new R(10);
  b = new R(20); // here
}
proc test_out_inout() {
  writeln();
  writeln("test_out_inout");
  var a = new R(1);
  var b = new R(2);
  out_inout(a, b);
  writeln(a, " ", b);
}
test_out_inout();
```

This deinitializes `new R(20)` inside of the body of `out_inout` (instead
of transferring that responsibility to the call site). As a result, the
deinitialization order for `new R(20)` is different (20 is deinitialized
before 10). The order of write-backs and other deinitializations at the
call site is still the same.

In the process of addressing problems with `inout` for arrays, I
identified several issues related to array views and copy semantics such
as #16185 and #16195. This caused me to make the copy changes described
above including removing `chpl__unref`. 

Here is a more detailed summary of changes:
 * Adjusts wrappers.cpp, normalize.cpp to use a single argument for inout
   handling
 * Adds logic to `getInstantiationType` that considers if the type should
   be used for a value to be returned/stored in a variable/passed by in
   intent. This calls `getCopyTypeDuringResolution` which keeps track of
   resolved `initCopy` calls and the types that they return. When the
   result of `initCopy` produces a different type, then the type needs
   special handling here. In particular, for example, `proc f(in arg)`
   called with an array view should instantiate with a non-view array
   (resulting from the copy).
 * Adjusts canCoerce to use `getCopyTypeDuringResolution` and to allow
   coercion from a type to the result of `initCopy` on that type when
   working with an `in`/`const in`/`inout` formal argument.
 * Removes `chpl__unalias` and most `chpl__unref` calls; replaces these
   with initCopy
 * For domains that are "borrowed" such as `A.domain`, use simple
   compile-time tracking of such domains to add an `initCopy` for
   returning the borrowed domain or passing it to `in` intent. Adds
   `isCallExprTemporary` and `isTemporaryFromNoCopyReturn` to implement
   this analysis.
 * adjust insertUnrefForArrayOrTupleReturn to use initCopy instead of
   `chpl__unalias` and to use the domain analysis to call it for cases
   like `A.domain`
 * adjusts split init to use the domain analysis for cases like
   `A.domain`
 * makes rank change and reindex arrays include `param ownsArrInstance`
   to distinguish between rank change/reindex arrays that own their
   elements vs those that do not. Adjusted isRankChangeArrayView /
   isReindexArrayView to return `false` for such arrays that own their
   elements. 
 * adds `PRIM_SET_ALIASING_ARRAY_ON_TYPE` and uses it in rank change /
   reindex array views so that the `FLAG_ALIASING_ARRAY` flag on the type
   is set appropriately and according to  `param ownsArrInstance.
 * move isAliasingArrayImplType and isAliasingArrayType to type.cpp so
   they can be used in more than one place.
 * add `FLAG_NO_COPY_RETURNS_OWNED` to work around an order-of-resolution
   issue.
 * removes an old, no longer necessary, workaround for initCopy functions
   in updateVariableAutoDestroy.
 * updated `--print-module-resolution` output to print out the path of
   modules being resolved for additional information. This is only
   currently tested in the test named print-module-resolution.chpl.
 * removed dead code in postFoldPrimop
 * adjusted tuple code to avoid copying ref tuples in tuple init
   functions and to copy all elements in initCopy functions
 * adjusts `<<=` and `>>=` functions to accept an integral shift amount
   the way that `<<` and `>>` do. This was to work around an issue that
   is no longer present but seemed like an improvement.
 * adjusts sync/single initializers to use concrete `const ref` intent
   for sync/single arguments instead of `const`. This was to work around
   an issue that is no longer present but seems like an improvement.

Reviewed by @e-kayrakli - thanks!

- [x] primers pass with verify+valgrind and do not leak
- [x] primers pass with gasnet
- [x] full local futures testing


Future work
 *  nested call in function call returning runtime type executes twice
    #16316
 * This PR makes some errors relating to returning tuples of owned
   runtime errors instead of compile-time errors. See #14942. This should
   be revisited after the resolution of #16233 / #15973.
 * #16339 about updating the spec description of how `ref` intent impacts
   candidate selection
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants