Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swiftのオーバーロード選択のスコア規則12種類

omochimetaru
December 13, 2019

 Swiftのオーバーロード選択のスコア規則12種類

omochimetaru

December 13, 2019
Tweet

More Decks by omochimetaru

Other Decks in Programming

Transcript

  1. SwiftͷΦʔόʔϩʔυબ୒ͷ
    είΞنଇ12छྨ
    @omochimetaru
    Θ͍Θ͍swiftc #16
    1

    View Slide

  2. Φʔόʔϩʔυͱܕਪ࿦
    • Swiftʹ͸Φʔόʔϩʔυ͕͋ΔͷͰɺίϯύΠ
    ϥ͸ɺ͋Δؔ਺ݺͼग़͕͠Ͳͷؔ਺ఆٛʹରԠ
    ͢Δ͔ਪ࿦͢Δɻ
    2

    View Slide

  3. • Ͳͷؔ਺ΛબͿ͔͸ܕਪ࿦ͱ૬ޓʹؔ܎͢Δͨ
    ΊɺΦʔόʔϩʔυબ୒͸ܕਪ࿦ػߏʹ౷߹͞
    Ε͍ͯΔɻ
    func f(_ a: Int) -> Int { return a }
    func f(_ a: Float) -> Float { return a }
    func main() {
    let a = f(Int(0))
    let b: Float = f(0)
    }
    3

    View Slide

  4. • ܕਪ࿦ث͸ɺΦʔόʔϩʔυͳͲ͕བྷΜͩ৔߹
    ʹɺෳ਺ͷ༗ޮͳղΛൃݟ͢Δ͜ͱ͕͋Δɻ
    func f(_ a: Int) -> Int { return a }
    func f(_ a: Int?) -> Int? { return a }
    func main() {
    let a = f(Int(0))
    }
    4

    View Slide

  5. • ෳ਺ͷ༗ޮͳղ͕͋Δ৔߹ɺ༏ઌ౓نଇʹΑΓ
    ࠷༏ઌղΛܾఆ͢Δɻ
    • ͦͷ༏ઌ౓نଇʹ͓͍ͯɺ࠷ॳʹద༻͞ΕΔ࠷
    ΋جຊͱͳΔنଇͱͯ͠ɺείΞنଇ͕͋Δɻ
    5

    View Slide

  6. είΞنଇ
    • 1ͭͷਪ࿦ղʹରͯ͠είΞΛܭࢉ͢Δɻ
    • είΞ͸12ݸͷ੔਺ͷ૊ɻ
    • ΑΓࠨͷܻ΄Ͳࢧ഑తɻ
    • ஋͕௿͍΄Ͳղͱͯ͠ͷ༏ઌ౓͕ߴ͍ɻϖφϧ
    ςΟ΍ίετͱଊ͑ΒΕΔɻ
    6

    View Slide

  7. func f(_ a: Int?) { print("Optional") }
    func f(_ a: Any) { print("Any") }
    f(3) // => Any
    7

    View Slide

  8. $ swiftc -dump-ast -Xfrontend -debug-constraints c1.swift
    --- Solution #0 ---
    Fixed score: 0 0 0 0 0 0 0 0 1 0 0 0
    Type variables:
    $T2 as () @ [email protected] [[email protected]:5:1 -> function result]
    $T1 as Int @ [email protected] [[email protected]:5:3]
    $T0 as (Int?) -> () @ [email protected] [[email protected]:5:1]
    --- Solution #1 ---
    Fixed score: 0 0 0 0 0 0 0 0 0 1 0 0
    Type variables:
    $T2 as () @ [email protected] [[email protected]:5:1 -> function result]
    $T1 as Int @ [email protected] [[email protected]:5:3]
    $T0 as (Any) -> () @ [email protected] [[email protected]:5:1]
    8

    View Slide

  9. 12छྨͷείΞ஋
    • 12ܻͷ஋ͦΕͧΕ͸ɺਪ࿦ղʹؚ·ΕΔ҉໧
    ม׵ͷճ਺ͳͲʹରԠ͍ͯ͠Δɻ
    • Ҏ߱Ͱ͸ɺ12छྨ͢΂ͯͷఆٛΛݟ͍ͯ͘ɻ
    9

    View Slide

  10. ϔομʔఆٛ1
    /// Describes an aspect of a solution that affects its overall score, i.e., a
    /// user-defined conversions.
    enum ScoreKind {
    // These values are used as indices into a Score value.
    /// A fix needs to be applied to the source.
    SK_Fix,
    /// A reference to an @unavailable declaration.
    SK_Unavailable,
    /// A use of a disfavored overload.
    SK_DisfavoredOverload,
    /// An implicit force of an implicitly unwrapped optional value.
    SK_ForceUnchecked,
    /// A user-defined conversion.
    SK_UserConversion,
    /// A non-trivial function conversion.
    SK_FunctionConversion,
    /// A literal expression bound to a non-default literal type.
    SK_NonDefaultLiteral,
    /// An implicit upcast conversion between collection types.
    SK_CollectionUpcastConversion,
    /// A value-to-optional conversion.
    SK_ValueToOptional,
    /// A conversion to an empty existential type ('Any' or '{}').
    SK_EmptyExistentialConversion,
    /// A key path application subscript.
    SK_KeyPathSubscript,
    /// A conversion from a string, array, or inout to a pointer.
    SK_ValueToPointerConversion,
    SK_LastScoreKind = SK_ValueToPointerConversion,
    };
    1 lib/Sema/ConstraintSystem.h
    10

    View Slide

  11. • SK_Fix͕࠷΋ߴίετɺ
    SK_ValueToPointerConversion͕࠷΋௿ίε
    τɻ
    • ҎԼศ্ٓɺ࠷΋௿ίετͳ
    SK_ValueToPointerConversionΛϥϯΫ1ͱ
    ͢Δɻ
    11

    View Slide

  12. ϥϯΫ1
    SK_ValueToPointerConversion
    • String, Array, inout͔ΒϙΠϯλ΁ͷ҉໧ม׵
    ίετ
    func f(_ a: UnsafePointer) { print("Pointer") }
    func f(_ a: [Int]) { print("Array") }
    let a: [Int] = [1, 2, 3]
    f(a) // => Array
    12

    View Slide

  13. (attempting disjunction choice $T0 bound to decl c2.(file)[email protected]:1:6 :
    (UnsafePointer) -> () at c2.swift:1:6 [[[email protected] [[email protected]:7:1]]];
    (overload set choice binding $T0 := (UnsafePointer) -> ())
    (increasing score due to value-to-pointer conversion)
    (found solution 0 0 0 0 0 0 0 0 0 0 0 1)
    )
    13

    View Slide

  14. ϥϯΫ2 SK_KeyPathSubscript
    • [keyPath: keyPath] ͷݺͼग़͠
    struct Cat {
    var name: String = "tama"
    }
    var a = Cat()
    let name = a[keyPath: \.name]
    print(name) // => tama
    ($T1 bindings={(supertypes of) Cat})
    Initial bindings: $T1 := Cat
    (attempting type variable $T1 := Cat
    (overload set choice binding $T2 := @lvalue String)
    ($T7 bindings={(supertypes of) WritableKeyPath})
    Initial bindings: $T7 := WritableKeyPath
    (attempting type variable $T7 := WritableKeyPath
    (found solution 0 0 0 0 0 0 0 0 0 0 1 0)
    )
    )
    14

    View Slide

  15. struct Cat {
    var name: String = "tama"
    subscript(keyPath keyPath: KeyPath) -> String {
    return "mike"
    }
    }
    var a = Cat()
    let name = a[keyPath: \.name]
    print(name) // => mike
    15

    View Slide

  16. ϥϯΫ3
    SK_EmptyExistentialConversion
    • Any΁ͷม׵
    func f(_ a: Any) { print("Any") }
    func f(_ a: UnsafePointer) { print("Pointer") }
    let a: [Int] = [1, 2, 3]
    f(a) // => Pointer
    16

    View Slide

  17. ϥϯΫ4 SK_ValueToOptional
    • Optional΁ͷม׵
    func f(_ a: Int?) { print("Optional") }
    func f(_ a: Any) { print("Any") }
    let a: Int = 1
    f(a) // => Any
    17

    View Slide

  18. ϥϯΫ5
    SK_CollectionUpcastConversion
    • Array, Dictionary, Setͷ҉໧ΞοϓΩϟετ
    class Animal {}
    class Cat: Animal {}
    func f(_ a: [Animal]) { print("[Animal]") }
    func f(_ a: [Cat]) { print("[Cat]") }
    let a: [Cat] = [Cat()]
    f(a) // => [Cat]
    18

    View Slide

  19. (attempting disjunction choice $T0 bound to decl rank5.(file)[email protected]:4:6 :
    ([Animal]) -> () at rank5.swift:4:6 [[[email protected] [[email protected]:10:1]]];
    (overload set choice binding $T0 := ([Animal]) -> ())
    (attempting disjunction choice [Cat] bind [Animal] [deep equality]
    [[[email protected] [[email protected]:10:1 -> apply argument -> comparing call argument #0 to parameter #0]]];
    )
    (attempting disjunction choice [Cat] arg conv [Animal] [array-upcast]
    [[[email protected] [[email protected]:10:1 -> apply argument -> comparing call argument #0 to parameter #0]]];
    (increasing score due to collection upcast conversion)
    (found solution 0 0 0 0 0 0 0 1 0 0 0 0)
    )
    )
    19

    View Slide

  20. • ෳ߹
    class Animal {}
    class Cat: Animal {}
    func f(_ a: [Cat?]) { print("[Cat?]") }
    func f(_ a: [Any]) { print("[Any]") }
    func f(_ a: [Animal]) { print("[Animal]") }
    let a: [Cat] = [Cat()]
    f(a) // => [Animal]
    20

    View Slide

  21. (attempting disjunction choice $T0 bound to decl rank5.(file)[email protected]:4:6 : ([Cat?]) -> () at rank5.swift:4:6
    [[[email protected] [[email protected]:12:1]]];
    (overload set choice binding $T0 := ([Cat?]) -> ())
    (attempting disjunction choice [Cat] bind [Cat?] [deep equality]
    [[[email protected] [[email protected]:12:1 -> apply argument -> comparing call argument #0 to parameter #0]]];
    )
    (attempting disjunction choice [Cat] arg conv [Cat?] [array-upcast]
    [[[email protected] [[email protected]:12:1 -> apply argument -> comparing call argument #0 to parameter #0]]];
    (increasing score due to collection upcast conversion)
    (increasing score due to value to optional)
    (found solution 0 0 0 0 0 0 0 1 1 0 0 0)
    )
    )
    (attempting disjunction choice $T0 bound to decl rank5.(file)[email protected]:6:6 : ([Any]) -> () at rank5.swift:6:6
    [[[email protected] [[email protected]:12:1]]];
    (overload set choice binding $T0 := ([Any]) -> ())
    (attempting disjunction choice [Cat] bind [Any] [deep equality]
    [[[email protected] [[email protected]:12:1 -> apply argument -> comparing call argument #0 to parameter #0]]];
    )
    (attempting disjunction choice [Cat] arg conv [Any] [array-upcast]
    [[[email protected] [[email protected]:12:1 -> apply argument -> comparing call argument #0 to parameter #0]]];
    (increasing score due to collection upcast conversion)
    (increasing score due to empty-existential conversion)
    (found solution 0 0 0 0 0 0 0 1 0 1 0 0)
    )
    )
    (attempting disjunction choice $T0 bound to decl rank5.(file)[email protected]:8:6 : ([Animal]) -> () at rank5.swift:8:6
    [[[email protected] [[email protected]:12:1]]];
    (overload set choice binding $T0 := ([Animal]) -> ())
    (attempting disjunction choice [Cat] bind [Animal] [deep equality]
    [[[email protected] [[email protected]:12:1 -> apply argument -> comparing call argument #0 to parameter #0]]];
    )
    (attempting disjunction choice [Cat] arg conv [Animal] [array-upcast]
    [[[email protected] [[email protected]:12:1 -> apply argument -> comparing call argument #0 to parameter #0]]];
    (increasing score due to collection upcast conversion)
    (found solution 0 0 0 0 0 0 0 1 0 0 0 0)
    )
    )
    21

    View Slide

  22. ϥϯΫ6 SK_NonDefaultLiteral
    • Ϧςϥϧ͕ඇσϑΥϧτܕʹͳΔ
    func f(_ a: Float) { print("Float") }
    func f(_ a: Int?) { print("Int?") }
    f(0) // => Int?
    22

    View Slide

  23. ϥϯΫ7 SK_FunctionConversion
    • ؔ਺ܕͷ҉໧ม׵
    class Animal {}
    class Cat: Animal {}
    // 0 0 0 0 0 1 0 0 1 0 0 0
    func f(_ g: () -> Cat?) { print("() -> Cat?") }
    // 0 0 0 0 0 1 0 0 0 0 0 0
    func f(_ g: () -> Animal) { print("() -> Animal") }
    // 0 0 0 0 0 0 0 0 1 0 0 0
    func f(_ g: (() -> Cat)?) { print("(() -> Cat)?") }
    func g() -> Cat { Cat() }
    f(g) // => (() -> Cat)?
    23

    View Slide

  24. ϥϯΫ8 SK_UserConversion
    • Objective-Cαϙʔτ࣌ʹɺϝλλΠϓ͔Β
    AnyObjectͳͲ΁ͷม׵
    class Cat {}
    func f(_ a: AnyObject) { print("AnyObject") }
    func f(_ a: Cat.Type) { print("Cat.Type") }
    f(Cat.self) // => Cat.Type
    24

    View Slide

  25. • ιʔε2
    if (getASTContext().LangOpts.EnableObjCInterop) {
    // These conversions are between concrete types that don't need further
    // resolution, so we can consider them immediately solved.
    auto addSolvedRestrictedConstraint
    = [&](ConversionRestrictionKind restriction) -> TypeMatchResult {
    addRestrictedConstraint(ConstraintKind::Subtype, restriction,
    type1, type2, locator);
    return getTypeMatchSuccess();
    };
    if (auto meta1 = type1->getAs()) {
    if (meta1->getInstanceType()->mayHaveSuperclass()
    && type2->isAnyObject()) {
    increaseScore(ScoreKind::SK_UserConversion);
    return addSolvedRestrictedConstraint(
    ConversionRestrictionKind::ClassMetatypeToAnyObject);
    }
    // Single @objc protocol value metatypes can be converted to the ObjC
    // Protocol class type.
    auto isProtocolClassType = [&](Type t) -> bool {
    if (auto classDecl = t->getClassOrBoundGenericClass())
    if (classDecl->getName() == getASTContext().Id_Protocol
    && classDecl->getModuleContext()->getName()
    == getASTContext().Id_ObjectiveC)
    return true;
    return false;
    };
    if (auto protoTy = meta1->getInstanceType()->getAs()) {
    if (protoTy->getDecl()->isObjC()
    && isProtocolClassType(type2)) {
    increaseScore(ScoreKind::SK_UserConversion);
    return addSolvedRestrictedConstraint(
    ConversionRestrictionKind::ProtocolMetatypeToProtocolClass);
    }
    }
    }
    if (auto meta1 = type1->getAs()) {
    // Class-constrained existential metatypes can be converted to AnyObject.
    if (meta1->getInstanceType()->isClassExistentialType()
    && type2->isAnyObject()) {
    increaseScore(ScoreKind::SK_UserConversion);
    return addSolvedRestrictedConstraint(
    ConversionRestrictionKind::ExistentialMetatypeToAnyObject);
    }
    }
    }
    2 lib/Sema/CSSimplify.cpp
    25

    View Slide

  26. User Conversion
    ޙड़
    26

    View Slide

  27. ϥϯΫ9 SK_ForceUnchecked
    • IUO(T!)ͷ҉໧unwrap
    // 0 0 0 1 0 0 0 0 0 0 0 0
    func f(_ a: Int) { print("Int") }
    func f(_ a: Int?) { print("Int?") }
    var a: Int! = 1
    f(a)
    27

    View Slide

  28. ϥϯΫ10 SK_DisfavoredOverload
    • @_disfavoredOverload͕෇͍ͯΔͱίετ͕
    ੜ͡Δ
    @_disfavoredOverload
    func f(_ a: Int) { print("Int") }
    func f(_ a: Int?) { print("Int?") }
    let a: Int = 1
    f(a) // => Int?
    28

    View Slide

  29. • ࠓ೥ͷ5݄ʹ࣮૷ 3
    • SwiftUIͰ࢖ΘΕͯΔ
    !
    // /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform \
    // /Developer/SDKs/iPhoneOS13.2.sdk/System/Library/Frameworks \
    // /SwiftUI.framework/Modules/SwiftUI.swiftmodule/arm64.swiftinterface
    @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
    extension Button where Label == SwiftUI.Text {
    public init(_ titleKey: SwiftUI.LocalizedStringKey, action: @escaping () -> Swift.Void)
    @_disfavoredOverload public init(_ title: S, action: @escaping () -> Swift.Void)
    where S : Swift.StringProtocol
    }
    3 https://github.com/apple/swift/pull/24799
    29

    View Slide

  30. ϥϯΫ11 SK_Unavailable
    • @availableͰແޮʹͳ͍ͬͯΔͱ෇͘ɻ
    • ࠷ऴతʹΤϥʔʹͳΔɻ
    // 0 1 0 0 0 0 0 0 0 0 0 0
    @available(*, unavailable)
    func f(_ a: Int) { print("Int") }
    let a: Int = 1
    f(a)
    /*
    rank11.swift:6:1: error: 'f' is unavailable
    f(a)
    ^
    rank11.swift:3:6: note: 'f' has been explicitly marked unavailable here
    func f(_ a: Int) { print("Int") }
    ^
    */
    30

    View Slide

  31. ϥϯΫ12 SK_Fix
    • ʮ΋͔ͯ͠͠ʯػೳͷͨΊʹੜ੒͞ΕͨԾઆʹ
    ෇༩͞ΕΔɻ
    func f(a: Int) { print("Int") }
    let a: Int = 1
    f(b: a)
    /*
    rank12.swift:4:2: error: incorrect argument label in call (have 'b:', expected 'a:')
    f(b: a)
    ^~
    a
    */
    31

    View Slide

  32. ---Constraint solving for the expression at [rank12.swift:4:1 - line:4:7]---
    (overload set choice binding $T0 := (Int) -> ())
    (overload set choice binding $T1 := Int)
    (attempting fix [fix: re-label argument(s)] @ [email protected] [[email protected]:4:1])
    (increasing score due to attempting to fix the source)
    Score: 1 0 0 0 0 0 0 0 0 0 0 0
    Type Variables:
    $T0 [lvalue allowed] as (Int) -> () @ [email protected] [[email protected]:4:1]
    $T1 [lvalue allowed] as Int @ [email protected] [[email protected]:4:6]
    $T2 as () @ [email protected] [[email protected]:4:1 -> function result]
    Active Constraints:
    Inactive Constraints:
    Resolved overloads:
    selected overload set choice a: $T1 == Int
    selected overload set choice f: $T0 == (Int) -> ()
    Fixes:
    [fix: re-label argument(s)] @ [email protected] [[email protected]:4:1]
    (found solution 1 0 0 0 0 0 0 0 0 0 0 0)
    32

    View Slide

  33. • ͜ͷFixػߏͷղઆ͕10݄ʹެࣜϒϩάͰެ։
    ͞Εͨ4
    4 New Diagnostic Architecture Overview
    33

    View Slide

  34. User Conversion
    34

    View Slide

  35. TypeChecker.rst5
    • Swiftͷਪ࿦ثʹ͍ͭͯॻ͔Εͨจॻ
    5 docs/TypeChecker.rst
    35

    View Slide

  36. • ͦ͜ʹग़ͯ͘ΔṖίʔυ
    struct X {
    // user-defined conversions
    func [conversion] __conversion () -> String { /* ... */ }
    func [conversion] __conversion () -> Int { /* ... */ }
    }
    func f(_ i : Int, s : String) { }
    var x : X
    f(10.5, x)
    36

    View Slide

  37. GitͰྺ࢙ௐࠪ
    37

    View Slide

  38. • User Conversionͷ࣮૷6
    Implement simplification of conversion constraints for user-defined
    conversions.
    Swift SVN r2730
    DougGregor committed on 24 Aug 2012
    6 fa474ee750edb7a3d34a767e02426f4509041698
    38

    View Slide

  39. • WWDCͰSwiftൃද 2014/6/2
    39

    View Slide

  40. • User Conversionͷېࢭ7
    Ban __conversion functions.
    Swift SVN r21015
    DougGregor committed on 5 Aug 2014
    7 9d5ba31daa8f63a333e9fb993e5204923a46f98c
    40

    View Slide

  41. • User Conversionͷ࡟আ8
    Remove user-defined conversions from the type checker.
    Swift SVN r21379
    DougGregor committed on 22 Aug 2014
    8 397f4a98880f82e439b1c4164885abb21cd56d09
    41

    View Slide

  42. • Swift1.0͕Xcode6ͱڞʹ഑෍ 2014/9/9
    42

    View Slide

  43. • SK_UserConversionΛݱࡏͷ༻్Ͱ࢖༻9
    Type checker: Increase the score of metatype-to-object conversions.
    Swift SVN r27415
    jckarter committed on 17 Apr 2015
    9 be7c339af86142217686f10e039349924226e9ea
    43

    View Slide

  44. • Swift2.0͕Xcode7ͱڞʹ഑෍ 2015/9/21
    44

    View Slide