Which tuple conversions should be customizable?
Summary of issue:
Generally it seems like most -- or perhaps all -- of our builtin conversions should behave as if they are final. For example, given:
class From {}
class To {}
impl From as ImplicitAs(To);
it does not seem like a good thing to allow impl (From, From) as ImplicitAs((To, To)) to customize the conversion. But how far should we take this?
For example:
- Should
impl (To, To) as ImplicitAs((From, From))be allowed? (Are we merely prioritizing builtin conversions then falling back to user-defined ones?) - Should
impl (From, From) as ImplicitAs((To, To, To))be allowed? (Do we commit to a built-in conversion just because the source and destination are tuples, or do we first check the arity matches?)
Details:
Our orphan rule allows customizing such conversions only if the source and/or destination type involves a user-defined type, but in the examples above, it does. With our current model for final (that is, that it doesn't prevent overlap, but merely prefers final impls in the overlap), we don't seem to have a great tool for the library to say "all (...) to (...) conversions are mine, but only some of them work", other than monomorphization failure.
Perhaps we should have a way of declaring the absence of an impl, that definitively says "if you find this, impl lookup fails and does not look elsewhere"?
Any other information that you want to share?
The toolchain currently tests whether the source and destination are tuples early in its conversion logic. If so, it commits to a builtin conversion, and produces errors on arity mismatch or if an element conversion would fail. If we added a fallback to user-defined conversions, it would not be straightforward to maintain the current diagnostic quality, because we'd have left the context in which the builtin conversion fails before we try the user-defined conversion.
Source: carbon-language/carbon-lang