Adding a constraint to a type resolution

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.

I think you can just call conformsToProtocol once you have a valid throws type from ThrowsTypeRequest:

// Only do this check if type is not Error or Never.
auto errorProto = ctx.getProtocol(KnownProtocolKind::Error);
bool conformsToError = TypeChecker::conformsToProtocol(errorTy, errorProto, DC);

I suppose you could do this in the request itself.

Hmm, I think, that this does not help in my case. But I just noticed, that I left out an important detail about what I want from this type resolution. Let me edit my first post real quick...

Do you know, if that approach would actually place a constraint on a generic type, so that a where E: Error gets synthesised?

No, but I think you can introduce a E: Error requirement for throws type in GenericSignatureBuilder::inferRequirements.

I think this: swift/GenericSignatureBuilder.cpp at d5731c5ff14ee8ba9aefa2c749dc490b31569530 · apple/swift · GitHub might help

Thanks for these suggestions. I will look into them.