Swiftの型システムを読む その20 - UnresolvedDotExprの型推論(制約生成〜Simplify)

December 22, 2017

前回の復習

E.aのように呼び出し元の式(BaseExpr, SubExpr)の型がわかっていいる状態でのメンバーの参照がある場合の.aUnresolvedDotExprとして扱われる。

制約生成

ConstraintGenerator::visitUnresolvedDotExprで制約が生成される。

self.initの場合かどうかで分かれていて、基本的にはそのままaddMemberRefConstraintsが呼ばれる。

if (CS.TC.getSelfForInitDelegationInConstructor(CS.DC, expr)) { 
... 
	return methodTy;
}
return addMemberRefConstraints(expr, expr->getBase(), expr->getName(),
                             expr->getFunctionRefKind());

Type addMemberRefConstraints(Expr *expr, Expr *base, DeclName name,
                             FunctionRefKind functionRefKind)

E.a の場合、

  • exprUnresolvedDotExpr, つまり.aを指す。
  • baseUnresolvedDeclRefExpr, つまりEを表す。
  • nameaを指す。

そこから、ConstraintSystem::addValueMemberConstraintが呼ばれる。 その際にmemberTy、つまり.aの型が型変数で置かれる。 メソッドであれば関数型、プロパティであればそのプロパティの型。

auto baseTy = CS.getType(base);
auto tv = CS.createTypeVariable(
            CS.getConstraintLocator(expr, ConstraintLocator::Member),
            TVO_CanBindToLValue |
            TVO_CanBindToInOut);
CS.addValueMemberConstraint(baseTy, name, tv, CurDC, functionRefKind,
    CS.getConstraintLocator(expr, ConstraintLocator::Member));
void addValueMemberConstraint(Type baseTy, DeclName name, Type memberTy,
                                DeclContext *useDC,
                                FunctionRefKind functionRefKind,
                                ConstraintLocatorBuilder locator)

ConstraintKindとしてはValueMemberというkindになる。

Simplify

いつも通りaddXXXConstraintされたらできるだけSimplifyされてからConstraintSystemに記録される。Memberの制約の場合はConstraintSystem::simplifyMemberConstraintがそれにあたる。

大まかな挙動としては

  1. ConstraintSystem::performMemberLookupでメンバーを見つけようとする
  2. 見つからなかったらaddUnresolvedConstraintされる。つまりInactiveConstraintsConstraintGraphに入る。(見つからなくて成功する場合とは…?基本不正な呼び出しと考えて良い?)
  3. 見つかったら候補がすべてaddOverloadSetされる。

ConstraintSystem::addOverloadSet

void ConstraintSystem::addOverloadSet(Type boundType,
                                      ArrayRef<OverloadChoice> choices,
                                      DeclContext *useDC,
                                      ConstraintLocator *locator,
                                      OverloadChoice *favoredChoice)

見つかった候補は個数に関わらずここに渡されてくる。

  1. 候補が1つの場合はそのままオーバーロードが解決されたとして話が進められる。
  2. 候補が複数の場合はaddDisjunctionConstraintされる。

-debug-constraintsでみてみると確かにOverload choices:の項目に出ていることがわかる。

Overload choices:

[email protected] [[email protected]:6:5 -> member] with unr2.(file)[email protected]:2:8 as Dog.bark: (String) -> ()

また複数の場合はDisjunctionな制約がaddUnresolvedConstraintされ、あとでどこかで選ばれるのだろう。今度調べる。

void addDisjunctionConstraint(ArrayRef<Constraint *> constraints,
                              ConstraintLocatorBuilder locator,
                              RememberChoice_t rememberChoice = ForgetChoice,
                              bool isFavored = false) {
  auto constraint =
    Constraint::createDisjunction(*this, constraints,
                                  getConstraintLocator(locator),
                                  rememberChoice);
  if (isFavored)
    constraint->setFavored();

  addUnsolvedConstraint(constraint);
}

ConstraintSystem::resolveOverload

名前がちょっとややこしいが、これはオーバーロードがすでに選択されて「それを解として使うよ」みたいなときに呼ばれるメソッド。

void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
                                       Type boundType,
                                       OverloadChoice choice,
                                       DeclContext *useDC)

boundTypeは型変数が入っている。

(lldb) po boundType->dump()
(type_variable_type id=0)

choiceは名前の通り選択されたオーバーロードを表している。

(lldb) po choice.getDecl()->dump()
(func_decl "bark(_:)" interface type='(Dog) -> (String) -> String' ...
(lldb) po choice.getKind()
Decl

ここからgetTypeOfMemberReferencegetTypeOfReferenceが呼び出されてrefTypeが求められる。これらの中身は単体でネタにできるくらいデカイのでまた次回以降で解説する。

(lldb) po refType->dump()
(function_type escaping
  (input=paren_type
    (struct_type decl=Swift.(file).String))
  (output=struct_type decl=Swift.(file).String))

refTypeが求まると、それについて改めてBindaddConstraintされる。

 addConstraint(ConstraintKind::Bind, boundType, refType, locator);

まとめ

  1. メンバーの型が型変数Tで置かれる
  2. resolveされてT := A -> B みたいにBindされる

制約生成までででかくなってしまったので今回はここまで。

このエントリーをはてなブックマークに追加