Description
Summary of issue:
It's possible for an impl to have a type structure that cannot be further refined (depending on your perspective it'd be maximal or minimal under the partial ordering by type structure; I'm going to consider "more refined" to be "greater" so that such type structures are maximal), and this can occur without the impl being unparameterized. This happens if the impl
's only parameters are non-type parameters. For example, in the prelude:
class Int(N:! IntLiteral()) {
adapt MakeInt(N);
}
impl forall [To:! IntLiteral()] IntLiteral() as ImplicitAs(Int(To)) {
fn Convert[self: Self]() -> Int(To) = "int.convert_checked";
}
Here, the type structure of this impl
is IntLiteral() as ImplicitAs(Int(?))
. The impl is parameterized, but this type structure is still maximal: at the type structure level, the value for a non-type argument of a type is always a ?
. It can still be specialized -- there could be an impl for IntLiteral() as ImplicitAs(Int(32))
, for example -- but only in the same file, and any such impl would need to be in a prioritization block with the above impl.
The consequence of this is that it is sound to permit such an impl
to be declared as final
. And it's useful to do so: this allows
final impl forall [N:! IntLiteral()]
IntLiteral() as AddWith(Int(N)) where .Result = Int(N);
... to be declared final, allowing us to know that the type of 1 + n
(for n: Int(N)
) is Int(N)
early in type-checking.
We should consider changing the final
rules to allow maximal but parameterized impl
s to be declared final
even if the root type or interface is not owned by the current file.
Details:
It's tempting to treat such impl
s as being effectively final, but we probably shouldn't: final
implies that information from the impl can be used earlier in type-checking, so probably should require an explicit opt-in from the author of the impl
.
Any other information that you want to share?
No response