Hey, I am currently trying to implement type-checking for the Typed throws proposal.
We already have the syntax parsed and AbstractFunctionDecl now contains a TypeLoc ThrowsType.
Now I want to type-check this throws type.
I think, the right place for that is the InterfaceTypeRequest::evaluate() function in TypeCheckDecl.cpp, because in there the result type and the types of the parameters of the function are getting type-checked. So, based on the implementation for the result type, I added a Type getThrowsInterfaceType() function to AbstractFunctionDecl, which just calls evaluateOrDefault on the newly added ThrowsTypeRequest class.
Now it gets interesting with the ThrowsTypeRequest::evaluate() function.
In there, I look on the AbstractFunctionDecl.
There are two easy cases for getting the throws type:
If the function does not throw, the type is resolved to Never.
Else if in parsing there was no type found, the type is defaulted to Swift.Error, because that's basically the current behaviour of throws.
Otherwise I have to use some type resolver, with a constraint, that the type must conform to Error. I dont know, how to do that.
My current code looks like that:
Type ThrowsTypeRequest::evaluate(Evaluator &evaluator,
AbstractFunctionDecl *decl) const {
auto &ctx = decl->getASTContext();
// Return Swift.Never, if the function doesnt throw.
if (!decl->hasThrows())
return ctx.getNeverType();
TypeRepr *throwsTyRepr = decl->getThrowsTypeRepr();
// If no type is specified, default to Swift.Error
if (throwsTyRepr == nullptr)
return ctx.getErrorDecl()->getInterfaceType();
const auto options =
TypeResolutionOptions(TypeResolverContext::FunctionThrows);
auto *const dc = decl->getInnermostDeclContext();
auto type = TypeResolution::forInterface(dc, options, /*unboundTyOpener*/ nullptr)
.resolveType(throwsTyRepr);
// the three statements above are basically copy-pasted from `ResultTypeRequest::evaluate`
// I don't know, if I can constrain the type already in the resolution
return type;
}
I also looked at the implementation of TypeResolver::resolveDictionaryType in TypeCheckType.cpp, as that function calls TypeChecker::applyUnboundGenericArguments, which constrains the key type of the dictionary to Hashable.
IIUC, somehow I have to create a TypeSubstitutionMap and fill it in with a GenericTypeParamType , that says, that our type has to conform to Error. I don't know, if this explanation is somewhat understandable or if it is correct.
I hope, somebody can help me with this.
EDIT:
I left out an important detail:
The type resolution of the dictionary type causes the compiler to synthesise a where clause.
If we have this function:
func foo<K>(_ bar: [K: Int]) {}
The compiler actually synthesises the following:
func foo<K>(_ bar: [K: Int]) where K: Hashable {}
because that is a constraint of the dictionary type.
Similarly, the proposal states, that this function:
func foo<E>() throws (E) {}
gets synthesised to that:
func foo<E>() throws (E) where E: Error {}
AFAIK, because of this, I can't just use e.g. TypeChecker::conformsToProtocol because it doesn't place a constraint on the generic type, at least, I couldn't find that in the compiler code. If that is a wrong assumption of mine, that would be great.