Explicit Copies Mode

I just saw this as it’s relevant to an issue I had recently.

The biggest question mark I have with this mode is: ultimately, what do you do when you find a copy that you think doesn’t belong? In the original example, you need to write for x in copy xs. Provided that xs is not mutated by the loop, I don’t see why there should be a copy.

In a private conversation, Joe said this might come along with a borrow operator, so presumably you may be able to write for x in borrow xs. If that’s the case, then we should be careful to ensure both copy and borrow are available together in this mode, or else some people will start baking in sub-optimal copy behavior where Swift could (eventually) infer a more correct borrow.

Ultimately, at this time, I wonder if this mode of operation is right for the people we expect to use the feature. I imagine it would mostly be used by compiler engineers to find areas of improvement, and developers writing Swift code under tight performance constraints. I’m personally more of the latter. I would be OK with a compiler mode flag that you can use on a one-off basis to show you where “big” copies are generated; “big” would be either “too big to fit in registers” or “causes refcount traffic”.

7 Likes

@kavon are there any updates?

1 Like

I have an explicit copies mode available as an experimental feature as of 6.4.0. It still needs some more work to make it usable enough to graduate into normal feature, but as of now it does highlight all copies as they would exist under -Onone. To try it out, you can either do:

  • -enable-experimental-feature ManualOwnership + on each function you care about, annotate with @diagnose(SemanticCopies, as: .warning)
  • -enable-experimental-feature ManualOwnership -Wwarning SemanticCopies to cover the whole build target

Background

The flag -enable-experimental-feature ManualOwnership won't get you any diagnostics by itself, as they're ignored by default! You then have to opt-in to the set of diagnostics you care about and the level of severity. First, there's two diagnostic groups:

  • SemanticCopies - the explicit copies mode discussed in this thread, where the copies correspond to ones in -Onone and you can acknowledge them with copy to silence the diagnostic.
  • DynamicExclusivity - flags any dynamic exclusivity checks that will happen at -Onone. There is not yet a language syntax to accept these checks, the diagnostic is purely informational.

So, to get the diagnostics to show where you want them, you also must either

  • Use -Wwarning/-Werror <diagnostic group> in your build settings.
  • Add @diagnose(<diagnostic group>, as: {error, warning, ignored}) on the particular declaration you're interested in.

Known Issues

There's a reason this is still experimental. I do plan to address many of the issues via work on a new move-checker, which is designed in such a way that it reliably canonicalizes copies everywhere.

As of today, I'm aware of issues with var's and generic types requiring many more copies to acknowledge than you'd expect, wrong variable names in the diagnostic message, and corner cases where it's impossible to silence the diagnostic with the copy expression. You can look at the FIXME's here for examples of the issues.

If you run into a compiler crash, you can throw @_noManualOwnership on the crashing function as a workaround.

13 Likes

Awesome, thank you for the effort you're putting into this.

1 Like