GSoC – Shared storage for property wrappers and @expanded parameters

GSoC is almost over (:smiling_face_with_tear:) and I wanted to share a little overview of the work I did, the experience in general, and what's next.

In summary:

  • I worked on the design and pitch of Shared Storage for property wrappers.
@propertyWrapper
struct Clamped<Value: Comparable> {
  // a plain struct, shared across wrapper instances
  @shared let storage: RangeStorage<Value>
  var wrappedValue: Value { ... }
}
class Pastry {
  init(emoji: String, ingredients: [String]) { ... }
  init(name: String, origin: String) { ... }
}

func bake(pastry: @expanded Pastry, qty: Int) { }

// new valid ways to call `bake(pastry:qty:)`
bake(emoji: "πŸ₯", ingredients: [🧈, πŸ’›], qty: 100)
bake(name: "Pastel", origin: "πŸ‡§πŸ‡·", qty: 42)

Next steps

Share the @expanded feature with the community, gather feedback and iterate on the design and implementation.

Behind the scenes of shared storage for property wrappers

The project started as a feature to allow property wrappers to reference their enclosing instance, but it changed a lot throughout the summer. Even though I knew property wrappers from a user perspective, proposing a change to it required me to deepen my understanding. So I invested hours reading previous proposals, discussions on the forum, and asked Holly, my mentor, a lot of questions. At this stage, I also had to start thinking about Swift as something I can actually change rather than just understand how it works.

While the core of the feature was already discussed by other members of the community, writing a pitch meant considering every single detail about it that I could think of. Defining how it works together with other features, what level of customization it should have, and how are people going to use it. The latter was perhaps what I was most curious about.

The shared storage pitch was my first post that received quite a lot of engagement, and discussing these ideas with the community was one of the highlights of this summer. I remember wanting to thank every person that commented – either for suggesting changes, saying they didn't like it or that it was interesting.

I learned from the feedbacks that this feature had some aspects that would be useful outside of property wrappers. I guess this was one of the reasons why it got broken down into a trilogy of proposals. Another branch of feedback questioned whether using subscripts would be better. And in fact, comparing previously suggested alternatives to the one I was proposing should've received more attention in the pitch.

Behind the scenes of @expanded

For the second part of GSoC, I focused on one of the three features that originated from the last pitch. @expanded is considerably simpler from a design perspective β€” it's all about adding special rules for argument to parameter matching.

My first idea was to look up the constructors available for the expanded type and use them to collect the appropriate arguments from the call. But since there may be multiple initializers and the arguments might need diagnostics, I ended up going with the approach of generating an implicit init call and gathering arguments based on them not matching the parameter next to @expanded. This implicit init call would then go through constraint generation and solution independently.

Working on the implementation was quite interesting. Often I would add a new test using @expanded differently and uncover cases that didn't work. These were the most fun because they weren't always supposed to be fixed, but rather an opportunity to decide what should be valid swifty code and what shouldn't. This is the type of question I remember discussing more often with Holly, and a lesson I take from it is that it's better to add support for something later than to remove it. But even the cases that weren't allowed might deserve special emoji diagnostics, so I still had to treat some of them in the compiler.

I went through this loop of adding a new test case > find bug > debug > solve it (or not) multiple times, and my workflow for debugging changed a lot. It is quite easy for me to go from one method definition to the other, come up with a bunch of ideas of what might be wrong, experiment, and forget how I got there (:upside_down_face:). Especially when methods have hundred of lines. So I started writing my own stack trace, taking notes of my assumptions, where exactly I was investigating and why, and where to look next. This technique will be useful in whatever project I work on next.

Closing thoughts

It can be daunting to approach compiler code for the first time while learning a new language. The types of problems that you run into are really different, but cool too. I remember spending quite some time figuring out how to keep argument expressions from being evaluated multiple times by the type checker. The strategy suggested by Holly was to wrap them somehow, which led me to explore a bunch of ways that could be done, and out of curiosity look for other Swift features that had to use similar techniques in its implementation. Fun trivia.

An interesting takeaway is that working on the code can serve as a design tool as well. Previously, for shared storage, I would try to anticipate corner cases just by thinking about how would people use them. But this time, ideas for the several ways it could be used (and broken) appeared as I worked on the implementation.

I'm grateful I had such an incredible mentor. @hborla would give me clear starting points and discuss alternatives as I progressed. I had no compiler background and no idea of what even is an AST, and her patience to explain these concepts (or anything else I asked!) was reassuring. Also, shoutout to @augusto2112 for the ninja and lldb tips and to @John_McCall for the language design master class. I learned so much every step of the way, and I hope to see other GSoC projects focused on language features in the future.

43 Likes

Great write up, and thanks for the hard work!

2 Likes

great work! expanded looks very cool, looking forward to seeing more.

3 Likes

Excellent work! Thank you for this awesome contribution!

I hope to see both shared storage and @expanded in Swift in the future!

You mention that the project started out by exploring accessing enclosing self. Do you know if there’s a proposal coming up for supporting the (currently pseudo private) way of doing that - or if there are some other ideas for allowing this?

Thanks for the write up!

3 Likes

Thanks! I don't know if the enclosing self feature is being explored at the moment. I do know that one of the initial ideas for shared storage was to implement it with a subscript. That could get us closer to an official way to access the enclosing instance.

3 Likes