[ Feature Request | Typechecker ] Add __NonAbstract as a built-in attribute for invoking static methods safely on reified types given `T as interfacename`
- Dominant language
- C++
- Stars
- 18.7k
- Forks
- 3.1k
- Avg merge
- 1h 47m
- Merged PRs (30d)
- 2
Description
**Is your feature request related to a problem? Please describe.**
Invoking a static method on the classname on an interface is allowed. This permits a situation where you invoke a purely abstract method (namely the interface method, instead of the static method on a class that implements this interface.) The same issue has been resolved for reified generics, but it never permits invoking a static method on the reified `T`, since it may be abstract. I wish to be able to restrict `T` to be a non-abstract type that implements a given interface.
This is releated to *runtime* #8711 and *typechecker* #8712
**Sample code**
```HACK
interface IFace {
public static function returnIt(): int;
}
function return_it_with_reified_generics(): void {
// This is a typechecker error, which is correct, but very limiting.
// Typing[4073] Cannot call IFace::returnIt(); it is abstract
T::returnIt();
$classname_of_t = \HH\ReifiedGenerics\get_classname();
// This is not a typechecker error,
// but this permits invoking `IFace::returnIt()`, which is abstract.
$classname_of_t::returnIt();
}
```
**Describe the solution you'd like**
We need something like `<<__NonAbstract>>`.
This requires the typechecker to check that the type which is being bound at the callsite is not abstract (or an interface).
This would make it possible to indicate to the typechecker that it is safe to invoke static methods on the `T`.
This sounds very close to `<<__Newable>>`, but it actually isn't.
`<<__Newable>>` can not be applied to a T with is `as IFace`ed, since `new IFace()` is not sound (you can't instantiate an interface).
If you make `T` in the reified example `<<__Newable>>`, you get this great descriptive error message:
```
Typing[4308] The type parameter T has the <<__Newable>> attribute.
Newable type parameters must be constrained with `as`,
and exactly one of those constraints must be a valid newable class.
The class must either be final,
or it must have the <<__ConsistentConstruct>> attribute
or extend a class that has it.
No constraints are valid newable classes
--> src/file.hack
55 | function return_it_with_reified_generics<<<__Newable>> reify T as IFace>(
|
```
Adding a `__ConsistentConstruct` to `IFace` doesn't help either, because `new IFace()` is still unsound, even if this constructor signature is known.
The `<<__NonAbstract>>`ness does not require runtime enforcement.
**Describe alternatives you've considered**
I can currently subvert the typeerror in an unsound way using this `\HH\ReifiedGenerics\get_classname()` "hack".
**Additional context**
Please refer to *runtime* #8711 and *typechecker* #8712 for more context.
How to make `classname::returnIt()` safe is an unsolved problem. I don't know how I would like this to be represented in the typesystem.
Also, the combination of `<<__Newable>>` and `<<__NonAbstract>>` on the same `T` could allow you to use newable on a type constrained to be a subtype of an interface. This is currently not allowed, since you can supply the interface type itself to `T`, which is unsound.
Contributor guide
Assessment
This issue has not been assessed yet.