Skip to content

Instantly share code, notes, and snippets.

@omochi
Last active August 11, 2018 06:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save omochi/d8bcc8dbdd7bddf390839ba035a3258a to your computer and use it in GitHub Desktop.
Save omochi/d8bcc8dbdd7bddf390839ba035a3258a to your computer and use it in GitHub Desktop.
func c(_ g: @escaping (Int) -> Void) {}

func a() {
    let f: ((Int) -> Void) -> Void
    
    f = { (g) -> Void in
        c(g)
    }
}

This code prints this useless error message.

error: cannot assign value of type '((Int) -> Void) -> Void' to type '((Int) -> Void) -> Void'

Expected is

error: cannot assign value of type '(@escaping (Int) -> Void) -> Void' to type '((Int) -> Void) -> Void'

I investigated this issue.

In this case, so type check is failed, type for error message is also reconstructed by ConstraintSystem.

CS say this.

`g` in declaration is ParenType( TypeVariableType(1) )
`g` in call of `c` is super type of (Int) -> Void
(TypeVariable ID is dummy for explanation)

CS find (Int) -> Void as possible binding of TypeVariableType(1). This is FunctionType and it has @escaping by representing isNoEscape == false internally. Finally, type of closure at right hand side of assignment is FunctionType ( ParenType( (Int) -> Void ) -> Void ).

Unfortunately, compiler prints this as ((Int -> Void) -> Void). To print (@escaping (Int -> Void) -> Void), isEscaping flag is needed at ParenType.

But in solving process, ConstraintSystem::simplifyType() just only replace TypeVariableType and does not affect ParenType at one outer side of this. ParenType stay here in first time for (g), ofcource it does not have isEscaping before solving constrains.


I try to rewrite type tree immediately after simplifyType for this pattern: FunctionType ( ParenType ( F1: FunctionType(...) ) -> * ), F1 is converted from TypeVariable. I have succeeded just rewriting. But it cause other problem.

Rewriting type tree breaks necessary equality relation.

func callMethodsOnOpaqueFunction<X, Y, U>(
  o: OpaqueFunction<X, Y>, 
  t: @escaping (X) -> Y, 
  u: U) 
{
  _ = o.inAndOutTuples(x: 
    (
      t, 
      { $0 }
    )
  )
}

class Opaque<T> {
  typealias ObnoxiousTuple = (T, (T) -> T)

  func inAndOutTuples(x: ObnoxiousTuple) -> ObnoxiousTuple { return x }
}

class OpaqueFunction<U, V>: Opaque<(U) -> V> {}

With this code, compiler fail AST verification.

tuple_expr element type mismatch:
  field: ((X) -> Y) -> (X) -> Y
  element: (@escaping (X) -> Y) -> (X) -> Y

I think that to solve this issue, ConstraintSystem has to track information about @escaping and whether it is explicit or implicit in AST. For example, ((Int) -> Void)? has implicit @escaping. And bind this information to type variable. To do this, current design around ParenType and FunctionType is looking bad. This idea is too hard for my skill so I suspended work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment