While working on implicit member chains, I've noticed that in CSGen, visitUnresolvedMemberExpr makes use of the RValueAdjustment
PathElementKind
. The documentation for this kind is sparse ("RValue adjustment."), and it's only used for UnresolvedMemberExpr
s and DynamicTypeExpr
s AFAICT, so I wanted to ask if someone with more knowledge could provide more context on this kind.
@Douglas_Gregor, digging through git blame indicates that you added this kind to visitUnresolvedMemberExpr
(7!) years ago, any chance you still remember what it's for?
That kind is used to indicate that equality/conversion constraint this element is associated with would always have r-value type on the right-hand side, left-hand side might be either l- or r-value. In particular, it's used in combination with UnresolvedMemberExpr
because such expressions supposed to produce the same type as contextual but contextual type could be an l-value type when base type and result of the reference could only be an r-value e.g.
enum E { case foo }
func test(e: inout E) {
if e == .foo { // although `e` is `l-value` both base and result of `.foo` are r-value types.
...
}
}
@xedin Is there an appropriate construction that goes in the other direction, i.e. allows the left-hand side of a conversion to be an r-value while the right-hand side is maybe-an-l-value?
More specifically, I've been rethinking the model for UnresolvedMemberExpr
s which have more members hanging off of them. The current model has the member type convertible to the base type and the base type equal to the context type, which works for a single-element chain. However, straightforwardly extending this model to multi-member chains doesn't play nice with optionals, e.g.,
struct S {
static var foo: S = S()
var x: S { S() }
}
let _: S? = .foo.x
This results in a nonsense error about (non-optional) foo
not being unwrapped, since its type was inferred as S?
. Instead, I'd like to rework the model so that each element in the chain need only be convertible to the next member, e.g., in the above example we would have that the implicit base converts to the result of .foo
, the result of .foo
converts to the result of .x
, the result of .x
converts to the context type.
However, I'm hitting a wall with this approach since the type of the .foo
member is actually @lvalue S
, which doesn't allow for a conversion from S
(unless I'm misunderstanding something?). What I'd like to express is "A convertible to B, ignoring any l-value-ness of B." Is such a conversion well-founded in this case?
Curious to know if this seems like a reasonable model for these chains, and if there's any other approach that you would suggest for modeling the type relationships here!
There is nothing in the language which could get from r-value to l-value type implicitly. I think only l-value -> r-value is possible by introducing an implicit load. In your example it seems like a result of .foo
needs to be a base and a result of .x
right? One way I see it could work is that each component should introduce a new result
type variable which actual result of the component is equated to, this way other components could only chain against that r-value type similar to what ConstraintGenerator::visitKeyPathExpr
does.
Yep, that's right. That extra level of indirection is what I settled on as well after some experimentation. Glad to know that's not totally misguided. Thank you!
No, I think that's totally fine!