Tuple types, compound names, and argument labels

I've been working on supporting tuple types with compound-named elements, and wanted to solicit feedback on my approach to a couple particular issues.

The goal is to be able to write something like the following:

var t: (f(x:): (Int) -> Void, x: Int) = (f(x:): { _ in }, x: 0)
t.f(x: t.x)

Currently, since tuple elements can't have compound names, the result of referencing a function-typed member of a tuple is always label-less. However, with compound-name elements, we need to change that model slightly.

For instance, in the above example, we want t.f to produce a function type with labels, so that the it matches the immediate call using the label x:. However, in most cases, we don't want tuple elements with compound names to apply labels to their arguments. E.g., in the line above:

var t: (f(x:): (Int) -> Void, x: Int) = (f(x:): { _ in }, x: 0)

we don't want the initializer expression to expect the argument to f(x:): to be a function type with labels, since function references provided there won't have any labels. Similarly, if we write:


we would want that to compile (just as it would if t were a struct with a method f(x: Int)).

My approach to this has been to extend FunctionRefKind to cover tuple index OverloadChoices, to differentiate between immediate application and other uses of tuple members. This is similar to the way we handle method references in getTypeOfMemberReference.

This works out pretty nicely, with a couple caveats which is primarily where I wanted feedback:

  • Tuples can be accessed with their index, e.g. t.0(x). In this case, we never want to apply labels even if there's a direct call. We could (ab)use FunctionRefKind::Compound, which has a very similar meaning, but that becomes confusing since tuples also support compound references (t.f(x:), as above). I added an additional case FunctionRefKind::TupleIndex, which spilled FunctionRefKind over four cases forcing me to expand the bitfields of all the expressions which use FunctionRefKind from 2 to 3 bits. Does adding the additional case for clarity seem worth the minor expansion in bitfield size?
  • Ignoring tuple element argument labels in all cases except direct application caused an issue in ASTVerifier.cpp. In t.f(x: 0), the TupleElementExpr t.f ends up with type (x: Int) -> Void, which breaks the check we do to verify that every TupleElementExpr has the same type as the type of the tuple element to which it refers. Since the latter had its labels stripped, I had to expose the FunctionRefKind of the TupleElementExpr to determine in verifyChecked whether we should strip the argument labels or not. Does this seem like a reasonable approach?
1 Like