Swiftの型システムを読む その21 - UnresolvedDotExprの型推論(Simplify~Expr書き換え)

December 25, 2017

前回の復習

  1. オーバーロードの候補が1つなら直ちにresolveOverloadをする
  2. オーバーロードの候補が2つ以上ならDisjunctionな制約生成後に、simplifyのタイミングでresolveOverloadする。

今回はこのあたりを中心に確認する。

  • Disjunctionからどうやってどれを使うか決めているか
  • resolveOverloadがしたら何が起こるか
  • Expr書き換え時の挙動は?

ConstraintSystem::solveSimplified

solve系の関数がいくつかあってややこしいが、solveSimplifiedsolveRecから呼ばれる関数。主な目的は型変数に具体的な型をバインディングすることみたい。ここでDisjunctionのうちの1つが選ばれる。

// Disjunctionをバラす
auto constraints = disjunction->getNestedConstraints();

// 一つ一つ取り出して、DisjunctionChoiceを作る。
for (auto index : indices(constraints)) {
    auto currentChoice = DisjunctionChoice(this, constraints[index]);
    // ....

DisjunctionChoiceに対してsolveが呼ばれる。ここからまたsimplifyされたりsolveRecされたりする。

if (auto score = currentChoice.solve(solutions, allowFreeTypeVariables)) {
  // ...
}
Optional<Score>
DisjunctionChoice::solve(SmallVectorImpl<Solution> &solutions,
                         FreeTypeVariableBinding allowFreeTypeVariables) {
  CS->simplifyDisjunctionChoice(Choice);
  bool failed = CS->solveRec(solutions, allowFreeTypeVariables);
  return failed ? None : Optional<Score>(CS->CurrentScore);
}

simplifyDisjunctionChoiceではさらにsimplifyConstraintにたどり着いて結果resolveOverloadされる。

case ConstraintKind::BindOverload:
  resolveOverload(constraint.getLocator(), constraint.getFirstType(),
                  constraint.getOverloadChoice(),
                  constraint.getOverloadUseDC());
  return SolutionKind::Solved;

ConstraintSystem::resolveOverload

上でresolveOverloadが呼ばれると、それはシンプルなBindに置き換えられる。

// Add the type binding constraint.
addConstraint(ConstraintKind::Bind, boundType, refType, locator);
  • boundTypeConstraintGenerator::visitUnresolvedXXXで作られた型変数を指す。
  • refTypeはその関数/メソッド/メンバーの型

また、ConstraintSystemresolvedOverloadSetsに記録され、Expr書き換えのタイミングで使用される。

resolvedOverloadSets
    = new (*this) ResolvedOverloadSetListItem{resolvedOverloadSets,
                                              boundType,
                                              choice,
                                              locator,
                                              openedFullType,
                                              refType};

Expr書きかえ

Expr書き換えでは、getOverloadChoiceIfAvailable getOverloadChoiceなどの関数経由で、上で記録したresolvedOverloadSetsが取得される。 それを使ってbuildMemberRefされる。

Expr *visitMemberRefExpr(MemberRefExpr *expr) {
  auto memberLocator = cs.getConstraintLocator(expr,
                                               ConstraintLocator::Member);
  auto selected = getOverloadChoice(memberLocator);
  bool isDynamic
    = selected.choice.getKind() == OverloadChoiceKind::DeclViaDynamic;
  return buildMemberRef(expr->getBase(),
                        selected.openedFullType,
                        expr->getDotLoc(),
                        selected.choice.getDecl(), expr->getNameLoc(),
                        selected.openedType,
                        cs.getConstraintLocator(expr),
                        memberLocator,
                        expr->isImplicit(),
                        selected.choice.getFunctionRefKind(),
                        expr->getAccessSemantics(),
                        isDynamic);
}

(タイトルはUnresolvedDotExprのになってるけど、短くてわかりやすいので代わりにvisitMemberRefExprを載せてます。ロジックはだいたい同じです。)

まとめ

  • ConstraintSystem::solveSimplifiedで1つ選ばれる
  • ConstraintSystem::resolveOverloadで型変数がバインドされ、書き換えのために記録される。
  • 記録された情報を元に、ASTを組み立てる
このエントリーをはてなブックマークに追加