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

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

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for omochimetaru omochimetaru
December 13, 2019

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

Avatar for omochimetaru

omochimetaru

December 13, 2019
Tweet

More Decks by omochimetaru

Other Decks in Programming

Transcript

  1. • Ͳͷؔ਺ΛબͿ͔͸ܕਪ࿦ͱ૬ޓʹؔ܎͢Δͨ ΊɺΦʔόʔϩʔυબ୒͸ܕਪ࿦ػߏʹ౷߹͞ Ε͍ͯΔɻ 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
  2. • ܕਪ࿦ث͸ɺΦʔόʔϩʔυͳͲ͕བྷΜͩ৔߹ ʹɺෳ਺ͷ༗ޮͳղΛൃݟ͢Δ͜ͱ͕͋Δɻ func f(_ a: Int) -> Int {

    return a } func f(_ a: Int?) -> Int? { return a } func main() { let a = f(Int(0)) } 4
  3. func f(_ a: Int?) { print("Optional") } func f(_ a:

    Any) { print("Any") } f(3) // => Any 7
  4. $ 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 () @ locator@0x7f9ce18abc58 [[email protected]:5:1 -> function result] $T1 as Int @ locator@0x7f9ce18abb98 [[email protected]:5:3] $T0 as (Int?) -> () @ locator@0x7f9ce18aba00 [[email protected]:5:1] --- Solution #1 --- Fixed score: 0 0 0 0 0 0 0 0 0 1 0 0 Type variables: $T2 as () @ locator@0x7f9ce18abc58 [[email protected]:5:1 -> function result] $T1 as Int @ locator@0x7f9ce18abb98 [[email protected]:5:3] $T0 as (Any) -> () @ locator@0x7f9ce18aba00 [[email protected]:5:1] 8
  5. ϔομʔఆٛ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
  6. ϥϯΫ1 SK_ValueToPointerConversion • String, Array, inout͔ΒϙΠϯλ΁ͷ҉໧ม׵ ίετ func f(_ a:

    UnsafePointer<Int>) { print("Pointer") } func f(_ a: [Int]) { print("Array") } let a: [Int] = [1, 2, 3] f(a) // => Array 12
  7. (attempting disjunction choice $T0 bound to decl c2.(file)[email protected]:1:6 : (UnsafePointer<Int>)

    -> () at c2.swift:1:6 [[locator@0x7f9daa815400 [[email protected]:7:1]]]; (overload set choice binding $T0 := (UnsafePointer<Int>) -> ()) (increasing score due to value-to-pointer conversion) (found solution 0 0 0 0 0 0 0 0 0 0 0 1) ) 13
  8. ϥϯΫ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<Cat, String>}) Initial bindings: $T7 := WritableKeyPath<Cat, String> (attempting type variable $T7 := WritableKeyPath<Cat, String> (found solution 0 0 0 0 0 0 0 0 0 0 1 0) ) ) 14
  9. struct Cat { var name: String = "tama" subscript(keyPath keyPath:

    KeyPath<Cat, String>) -> String { return "mike" } } var a = Cat() let name = a[keyPath: \.name] print(name) // => mike 15
  10. ϥϯΫ3 SK_EmptyExistentialConversion • Any΁ͷม׵ func f(_ a: Any) { print("Any")

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

    } func f(_ a: Any) { print("Any") } let a: Int = 1 f(a) // => Any 17
  12. ϥϯΫ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
  13. (attempting disjunction choice $T0 bound to decl rank5.(file)[email protected]:4:6 : ([Animal])

    -> () at rank5.swift:4:6 [[locator@0x7fbeeb0aa000 [[email protected]:10:1]]]; (overload set choice binding $T0 := ([Animal]) -> ()) (attempting disjunction choice [Cat] bind [Animal] [deep equality] [[locator@0x7fbeeb0aa3c8 [[email protected]:10:1 -> apply argument -> comparing call argument #0 to parameter #0]]]; ) (attempting disjunction choice [Cat] arg conv [Animal] [array-upcast] [[locator@0x7fbeeb0aa3c8 [[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
  14. • ෳ߹ 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
  15. (attempting disjunction choice $T0 bound to decl rank5.(file)[email protected]:4:6 : ([Cat?])

    -> () at rank5.swift:4:6 [[locator@0x7fed5c001c00 [[email protected]:12:1]]]; (overload set choice binding $T0 := ([Cat?]) -> ()) (attempting disjunction choice [Cat] bind [Cat?] [deep equality] [[locator@0x7fed5c002040 [[email protected]:12:1 -> apply argument -> comparing call argument #0 to parameter #0]]]; ) (attempting disjunction choice [Cat] arg conv [Cat?] [array-upcast] [[locator@0x7fed5c002040 [[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 [[locator@0x7fed5c001c00 [[email protected]:12:1]]]; (overload set choice binding $T0 := ([Any]) -> ()) (attempting disjunction choice [Cat] bind [Any] [deep equality] [[locator@0x7fed5c002040 [[email protected]:12:1 -> apply argument -> comparing call argument #0 to parameter #0]]]; ) (attempting disjunction choice [Cat] arg conv [Any] [array-upcast] [[locator@0x7fed5c002040 [[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 [[locator@0x7fed5c001c00 [[email protected]:12:1]]]; (overload set choice binding $T0 := ([Animal]) -> ()) (attempting disjunction choice [Cat] bind [Animal] [deep equality] [[locator@0x7fed5c002040 [[email protected]:12:1 -> apply argument -> comparing call argument #0 to parameter #0]]]; ) (attempting disjunction choice [Cat] arg conv [Animal] [array-upcast] [[locator@0x7fed5c002040 [[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
  16. ϥϯΫ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
  17. ϥϯΫ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
  18. • ιʔε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<MetatypeType>()) { 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<ProtocolType>()) { if (protoTy->getDecl()->isObjC() && isProtocolClassType(type2)) { increaseScore(ScoreKind::SK_UserConversion); return addSolvedRestrictedConstraint( ConversionRestrictionKind::ProtocolMetatypeToProtocolClass); } } } if (auto meta1 = type1->getAs<ExistentialMetatypeType>()) { // 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
  19. ϥϯΫ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
  20. ϥϯΫ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
  21. • ࠓ೥ͷ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<S>(_ title: S, action: @escaping () -> Swift.Void) where S : Swift.StringProtocol } 3 https://github.com/apple/swift/pull/24799 29
  22. ϥϯΫ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
  23. ϥϯΫ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
  24. ---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)] @ locator@0x7fdfbb0e55b0 [[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) -> () @ locator@0x7fdfbb0e5400 [[email protected]:4:1] $T1 [lvalue allowed] as Int @ locator@0x7fdfbb0e5488 [[email protected]:4:6] $T2 as () @ locator@0x7fdfbb0e5510 [[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)] @ locator@0x7fdfbb0e55b0 [[email protected]:4:1] (found solution 1 0 0 0 0 0 0 0 0 0 0 0) 32
  25. • ͦ͜ʹग़ͯ͘ΔṖίʔυ 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
  26. • User Conversionͷ࣮૷6 Implement simplification of conversion constraints for user-defined

    conversions. Swift SVN r2730 DougGregor committed on 24 Aug 2012 6 fa474ee750edb7a3d34a767e02426f4509041698 38
  27. • User Conversionͷېࢭ7 Ban __conversion functions. Swift SVN r21015 DougGregor

    committed on 5 Aug 2014 7 9d5ba31daa8f63a333e9fb993e5204923a46f98c 40
  28. • User Conversionͷ࡟আ8 Remove user-defined conversions from the type checker.

    Swift SVN r21379 DougGregor committed on 22 Aug 2014 8 397f4a98880f82e439b1c4164885abb21cd56d09 41
  29. • 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