Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
consistently disallow rebinding type-bound ops (#891)
## Summary Consistently disallow providing a generic type-bound operator implementation for generic types where one of its instances had the operator implicitly lifted already. This is how it already worked for parametric nominal types where the parameter is not used in the body (see `tinvalid_rebind.nim`). The design of type-bound operators at the language-level is still sorted out, and until that's done, it's better to have their behaviour be consistent. ## Details Rebinding for parametric nominal types where none of the parameters is used in the type's body was only disallowed because of a side-effect of how types are currently instantiated: if they don't contain generic parameters, the `PType` is not copied, meaning that the underlying `tyObject` for a `type Obj[T] = object` is the exact same for both the original type and all its instances. Therefore, when an operator is implicitly lifted, it is, effectively, bound to the operator slot of all instances plus that of the generic type, thus blocking binding a custom generic implementation. If the `PType` is copied during the instantiation process (as is the case for all parametric nominal types where at least one parameter is used in the type's body), only the slot of the instance is assigned to. In order to make the behaviour consistent, when producing the symbol for an operator during lifting (`produceSym`), it is checked whether the lifted-for nominal type is coming from a generic (indicated by the `tfFromGeneric` flag), and if it is, a pseudo symbol is assigned to the originating-from `PType`. The originating-from type is stored as the last position of a `tyGenericBody`, which is itself stored in the first position of a `tyGenericInst` -- the `tyGenericInst` for an instantiated type is accessible via `TType.typeInst`. Parametric nominal types where none of the parameters are used in the type's body don't have the `tfFromGeneric` flag set, and they thus keep working as they did previously. The pseudo-operator blocks the operator slot of the generic type, preventing a custom implementation from being bound *explicitly*. So that the pseudo-operator is not copied to a new instance during generic instantiation (which would disable the implicit operator lifting), `copyTypeProps` looks for the `sfAnon` flag (which is only included for pseudo-operators) on the to-be-copied operators, and if the flag is set, doesn't copy the operator. The tests for operator binding are expanded to make sure that explicitly binding a generic operator implementation keeps working when a custom implementation was already *explicitly* bound to specific instantiation of the type.
- Loading branch information