Slide 1

Slide 1 text

PNPDIJNFUBSV!Θ͍Θ͍TXJGD ܕਪ࿦ͷͪΐͬͱਂ͍࿩

Slide 2

Slide 2 text

લճͷ࿩ w 4XJGUͷܕਪ࿦Λֶ΅͏!USZ4XJGU5PLZP w ޷ධͰͨ͠🙇 w ౰ॳܭը͔Β͸ᷓΕͯΧοτͨ͠ w ࠓ೔ͷ࿩Χοτͨ͠ଓ͖ͷ෦෼

Slide 3

Slide 3 text

ࠓ೔ͷ࿩ w Φʔόʔϩʔυબ୒ɺδΣωϦοΫγάωνϟแؚؔ܎ w ܕਪ࿦രൃ w Ϧςϥϧͷਪ࿦ w ෳ਺จΫϩʔδϟͷਪ࿦ w JGࣜͷਪ࿦ w ܾఆෆೳ໰୊

Slide 4

Slide 4 text

Φʔόʔϩʔυબ୒ func foo(_ x: Int?) { print("x is Optional of Int") } func foo(_ x: Any) { print("x is Any") } foo(1) 4UBSU $T0 := (Int?) -> Void $T0 := (Int?) -> Void 4VDDFTT 4VDDFTT

Slide 5

Slide 5 text

ղͷൺֱ 4PMVUJPO$PNQBSF3FTVMU$POTUSBJOU4ZTUFNDPNQBSF4PMVUJPOT $POTUSBJOU4ZTUFNDT "SSBZ3FG4PMVUJPOTPMVUJPOT DPOTU4PMVUJPO%J ff EJ ff VOTJHOFEJEY VOTJHOFEJEY SolutionCompareResult ConstraintSystem::compareSolutions( ConstraintSystem &cs, ArrayRef solutions, const SolutionDiff &diff, unsigned idx1, unsigned idx2) { if (cs.isDebugMode()) { llvm::errs().indent(cs.solverState->getCurrentIndent()) << "comparing solutions " << idx1 << " and " << idx2 << "\n"; } // Whether the solutions are identical. bool identical = true; // Compare the fixed scores by themselves. if (solutions[idx1].getFixedScore() != solutions[idx2].getFixedScore()) { return solutions[idx1].getFixedScore() < solutions[idx2].getFixedScore() ? SolutionCompareResult::Better : SolutionCompareResult::Worse; } // Compute relative score. unsigned score1 = 0; unsigned score2 = 0; auto foundRefinement1 = false; auto foundRefinement2 = false; bool isStdlibOptionalMPlusOperator1 = false; bool isStdlibOptionalMPlusOperator2 = false; bool isVarAndNotProtocol1 = false; bool isVarAndNotProtocol2 = false; auto getWeight = [&](ConstraintLocator *locator) -> unsigned { if (auto *anchor = locator->getAnchor().dyn_cast()) { auto weight = cs.getExprDepth(anchor); if (weight) return *weight + 1; } return 1; }; SmallVector overloadDiff(diff.overloads); // Single type of keypath dynamic member lookup could refer to different // member overloads, we have to do a pair-wise comparison in such cases // otherwise ranking would miss some viable information e.g. // `_ = arr[0..<3]` could refer to subscript through writable or read-only // key path and each of them could also pick overload which returns `Slice` // or `ArraySlice` (assuming that `arr` is something like `Box<[Int]>`). addKeyPathDynamicMemberOverloads(solutions, idx1, idx2, overloadDiff); // Compare overload sets. for (auto &overload : overloadDiff) { unsigned weight = getWeight(overload.locator); auto choice1 = overload.choices[idx1]; auto choice2 = overload.choices[idx2]; // If the systems made the same choice, there's nothing interesting here. if (sameOverloadChoice(choice1, choice2)) continue; // If constraint system is underconstrained e.g. because there are // editor placeholders, it's possible to end up with multiple solutions // where each ambiguous declaration is going to have its own overload kind: // // func foo(_: Int) -> [Int] { ... } // func foo(_: Double) -> (result: String, count: Int) { ... } // // _ = foo(<#arg#>).count // // In this case solver would produce 2 solutions: one where `count` // is a property reference on `[Int]` and another one is tuple access // for a `count:` element. if (choice1.isDecl() != choice2.isDecl()) return SolutionCompareResult::Incomparable; auto decl1 = choice1.getDecl(); auto dc1 = decl1->getDeclContext(); auto decl2 = choice2.getDecl(); auto dc2 = decl2->getDeclContext(); // The two systems are not identical. If the decls in question are distinct // protocol members, let the checks below determine if the two choices are // 'identical' or not. This allows us to structurally unify disparate // protocol members during overload resolution. // FIXME: Along with the FIXME below, this is a hack to work around // problems with restating requirements in protocols. identical = false; if (cs.isForCodeCompletion()) { // Don't rank based on overload choices of function calls that contain the // code completion token. ASTNode anchor = simplifyLocatorToAnchor(overload.locator); if (auto expr = getAsExpr(anchor)) { // If the anchor is a called function, also don't rank overload choices // if any of the arguments contain the code completion token. if (auto apply = dyn_cast_or_null(cs.getParentExpr(expr))) { if (apply->getFn() == expr) { anchor = apply; } } } if (anchor && cs.containsIDEInspectionTarget(anchor)) { continue; } } bool decl1InSubprotocol = false; bool decl2InSubprotocol = false; if (dc1->getContextKind() == DeclContextKind::GenericTypeDecl && dc1->getContextKind() == dc2->getContextKind()) { auto pd1 = dyn_cast(dc1); auto pd2 = dyn_cast(dc2); // FIXME: This hack tells us to prefer members of subprotocols over // those of the protocols they inherit, if all else fails. // If we were properly handling overrides of protocol members when // requirements get restated, it would not be necessary. if (pd1 && pd2 && pd1 != pd2) { identical = true; decl1InSubprotocol = pd1->inheritsFrom(pd2); decl2InSubprotocol = pd2->inheritsFrom(pd1); } } // If the kinds of overload choice don't match... if (choice1.getKind() != choice2.getKind()) { identical = false; // A declaration found directly beats any declaration found via dynamic // lookup, bridging, or optional unwrapping. if ((choice1.getKind() == OverloadChoiceKind::Decl) && (choice2.getKind() == OverloadChoiceKind::DeclViaDynamic || choice2.getKind() == OverloadChoiceKind::DeclViaBridge || choice2.getKind() == OverloadChoiceKind::DeclViaUnwrappedOptional)) { score1 += weight; continue; } if ((choice1.getKind() == OverloadChoiceKind::DeclViaDynamic || choice1.getKind() == OverloadChoiceKind::DeclViaBridge || choice1.getKind() == OverloadChoiceKind::DeclViaUnwrappedOptional) && choice2.getKind() == OverloadChoiceKind::Decl) { score2 += weight; continue; } if (choice1.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup) { if (choice2.getKind() == OverloadChoiceKind::DynamicMemberLookup) // Dynamic member lookup through a keypath is better than one using // string because it carries more type information. score1 += weight; else // Otherwise let's prefer non-dynamic declaration. score2 += weight; continue; } if (choice2.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup) { if (choice1.getKind() == OverloadChoiceKind::DynamicMemberLookup) // Dynamic member lookup through a keypath is better than one using // string because it carries more type information. score2 += weight; else // Otherwise let's prefer non-dynamic declaration. score1 += weight; continue; } continue; }

Slide 6

Slide 6 text

// The kinds of overload choice match, but the contents don't. switch (choice1.getKind()) { case OverloadChoiceKind::TupleIndex: case OverloadChoiceKind::MaterializePack: case OverloadChoiceKind::ExtractFunctionIsolation: continue; case OverloadChoiceKind::KeyPathApplication: llvm_unreachable("Never considered different"); case OverloadChoiceKind::DeclViaDynamic: case OverloadChoiceKind::Decl: case OverloadChoiceKind::DeclViaBridge: case OverloadChoiceKind::DeclViaUnwrappedOptional: case OverloadChoiceKind::DynamicMemberLookup: case OverloadChoiceKind::KeyPathDynamicMemberLookup: break; } // We don't apply some ranking rules to overloads found through dynamic // lookup in order to keep a few potentially ill-formed cases ambiguous. bool isDynamicOverloadComparison = choice1.getKind() == OverloadChoiceKind::DeclViaDynamic && choice2.getKind() == OverloadChoiceKind::DeclViaDynamic; // Determine whether one declaration is more specialized than the other. bool firstAsSpecializedAs = false; bool secondAsSpecializedAs = false; if (isDeclAsSpecializedAs(cs.DC, decl1, decl2, isDynamicOverloadComparison, /*allowMissingConformances=*/false)) { score1 += weight; firstAsSpecializedAs = true; } if (isDeclAsSpecializedAs(cs.DC, decl2, decl1, isDynamicOverloadComparison, /*allowMissingConformances=*/false)) { score2 += weight; secondAsSpecializedAs = true; } // If each is as specialized as the other, and both are constructors, // check the constructor kind. if (firstAsSpecializedAs && secondAsSpecializedAs) { if (auto ctor1 = dyn_cast(decl1)) { if (auto ctor2 = dyn_cast(decl2)) { if (ctor1->getInitKind() != ctor2->getInitKind()) { if (ctor1->getInitKind() < ctor2->getInitKind()) score1 += weight; else score2 += weight; } else if (ctor1->getInitKind() == CtorInitializerKind::Convenience) { // If both are convenience initializers, and the instance type of // one is a subtype of the other's, favor the subtype constructor. auto resType1 = ctor1->mapTypeIntoContext( ctor1->getResultInterfaceType()); auto resType2 = ctor2->mapTypeIntoContext( ctor2->getResultInterfaceType()); if (!resType1->isEqual(resType2)) { if (TypeChecker::isSubtypeOf(resType1, resType2, cs.DC)) { score1 += weight; } else if (TypeChecker::isSubtypeOf(resType2, resType1, cs.DC)) { score2 += weight; } } } } } } // If both declarations come from Clang, and one is a type and the other // is a function, prefer the function. if (decl1->hasClangNode() && decl2->hasClangNode() && ((isa(decl1) && isa(decl2)) || (isa(decl1) && isa(decl2)))) { if (isa(decl1)) score2 += weight; else score1 += weight; } // A class member is always better than a curried instance member. // If the members agree on instance-ness, a property is better than a // method (because a method is usually immediately invoked). if (!decl1->isInstanceMember() && decl2->isInstanceMember()) score1 += weight; else if (!decl2->isInstanceMember() && decl1->isInstanceMember()) score2 += weight; else if (isa(decl1) && isa(decl2)) score1 += weight; else if (isa(decl2) && isa(decl1)) score2 += weight; // If both are class properties with the same name, prefer // the one attached to the subclass because it could only be // found if requested directly. if (!decl1->isInstanceMember() && !decl2->isInstanceMember()) { if (isa(decl1) && isa(decl2)) { auto *nominal1 = dc1->getSelfNominalTypeDecl(); auto *nominal2 = dc2->getSelfNominalTypeDecl(); if (nominal1 && nominal2 && nominal1 != nominal2) { auto base1 = nominal1->getDeclaredType(); auto base2 = nominal2->getDeclaredType(); if (isNominallySuperclassOf(base1, base2)) score2 += weight; if (isNominallySuperclassOf(base2, base1)) score1 += weight; } } } // If we haven't found a refinement, record whether one overload is in // any way more constrained than another. We'll only utilize this // information in the case of a potential ambiguity. if (!(foundRefinement1 && foundRefinement2)) { if (isDeclMoreConstrainedThan(decl1, decl2)) { foundRefinement1 = true; } if (isDeclMoreConstrainedThan(decl2, decl1)) { foundRefinement2 = true; } } // FIXME: The rest of the hack for restating requirements. if (!(foundRefinement1 && foundRefinement2)) { if (identical && decl1InSubprotocol != decl2InSubprotocol) { foundRefinement1 = decl1InSubprotocol; foundRefinement2 = decl2InSubprotocol; } } // Swift 4.1 compatibility hack: If everything else is considered equal, // favour a property on a concrete type over a protocol property member. // // This hack is required due to changes in shadowing behaviour where a // protocol property member will no longer shadow a property on a concrete // type, which created unintentional ambiguities in 4.2. This hack ensures // we at least keep these cases unambiguous in Swift 5 under Swift 4 // compatibility mode. Don't however apply this hack for decls found through // dynamic lookup, as we want the user to have to disambiguate those. // // This is intentionally narrow in order to best preserve source // compatibility under Swift 4 mode by ensuring we don't introduce any new // ambiguities. This will become a more general "is more specialised" rule // in Swift 5 mode. if (!cs.getASTContext().isSwiftVersionAtLeast(5) && choice1.getKind() != OverloadChoiceKind::DeclViaDynamic && choice2.getKind() != OverloadChoiceKind::DeclViaDynamic && isa(decl1) && isa(decl2)) { auto *nominal1 = dc1->getSelfNominalTypeDecl(); auto *nominal2 = dc2->getSelfNominalTypeDecl(); if (nominal1 && nominal2 && nominal1 != nominal2) { isVarAndNotProtocol1 = !isa(nominal1); isVarAndNotProtocol2 = !isa(nominal2); } } // FIXME: Lousy hack for ?? to prefer the catamorphism (flattening) // over the mplus (non-flattening) overload if all else is equal. if (decl1->getBaseName() == "??") { assert(decl2->getBaseName() == "??"); auto check = [](const ValueDecl *VD) -> bool { if (!VD->getModuleContext()->isStdlibModule()) return false; auto fnTy = VD->getInterfaceType()->castTo(); if (!fnTy->getResult()->getOptionalObjectType()) return false; // Check that the standard library hasn't added another overload of // the ?? operator. auto params = fnTy->getParams(); assert(params.size() == 2); auto param1 = params[0].getParameterType(); auto param2 = params[1].getParameterType()->castTo(); assert(param1->getOptionalObjectType()); assert(params[1].isAutoClosure()); assert(param2->getResult()->getOptionalObjectType()); (void) param1; (void) param2; return true; }; isStdlibOptionalMPlusOperator1 = check(decl1); isStdlibOptionalMPlusOperator2 = check(decl2); } } // Compare the type variable bindings. llvm::DenseMap typeDiff; const auto &bindings1 = solutions[idx1].typeBindings; const auto &bindings2 = solutions[idx2].typeBindings; for (const auto &binding1 : bindings1) { auto *typeVar = binding1.first; auto *loc = typeVar->getImpl().getLocator();

Slide 7

Slide 7 text

// All other things considered equal, if any overload choice is more // more constrained than the other, increment the score. if (score1 == score2) { if (foundRefinement1) { ++score1; } if (foundRefinement2) { ++score2; } } // FIXME: All other things being equal, prefer the catamorphism (flattening) // overload of ?? over the mplus (non-flattening) overload. if (score1 == score2) { // This is correct: we want to /disprefer/ the mplus. score2 += isStdlibOptionalMPlusOperator1; score1 += isStdlibOptionalMPlusOperator2; } // All other things being equal, apply the Swift 4.1 compatibility hack for // preferring var members in concrete types over a protocol requirement // (see the comment above for the rationale of this hack). if (!cs.getASTContext().isSwiftVersionAtLeast(5) && score1 == score2) { score1 += isVarAndNotProtocol1; score2 += isVarAndNotProtocol2; } // FIXME: There are type variables and overloads not common to both solutions // that haven't been considered. They make the systems different, but don't // affect ranking. We need to handle this. // If the scores are different, we have a winner. if (score1 != score2) { return score1 > score2? SolutionCompareResult::Better : SolutionCompareResult::Worse; } // Neither system wins; report whether they were identical or not. return identical? SolutionCompareResult::Identical : SolutionCompareResult::Incomparable; } // Check whether this is the overload type for a short-form init call // 'X(...)' or 'self.init(...)' call. auto isShortFormOrSelfDelegatingConstructorBinding = false; if (auto initMemberTypeElt = loc->getLastElementAs()) { isShortFormOrSelfDelegatingConstructorBinding = initMemberTypeElt->isShortFormOrSelfDelegatingConstructor(); } // If the type variable isn't one for which we should be looking at the // bindings, don't. if (!typeVar->getImpl().prefersSubtypeBinding() && !isShortFormOrSelfDelegatingConstructorBinding) { continue; } // If both solutions have a binding for this type variable // let's consider it. auto binding2 = bindings2.find(typeVar); if (binding2 == bindings2.end()) continue; TypeBindingsToCompare typesToCompare(binding1.second, binding2->second); // For short-form and self-delegating init calls, we want to prefer // parameter lists with subtypes over supertypes. To do this, compose tuples // for the bound parameter lists, and compare them in the type diff. This // logic preserves the behavior of when we used to bind the parameter list // as a tuple to a TVO_PrefersSubtypeBinding type variable for such calls. // FIXME: We should come up with a better way of doing this, though note we // have some ranking and subtyping rules specific to tuples that we may need // to preserve to avoid breaking source. if (isShortFormOrSelfDelegatingConstructorBinding) { auto diffs = getConstructorParamsAsTuples( cs.getASTContext(), typesToCompare.Type1, typesToCompare.Type2); if (!diffs) continue; typesToCompare = *diffs; } if (!typesToCompare.areSameTypes()) typeDiff.insert({typeVar, typesToCompare}); } for (auto &binding : typeDiff) { auto types = binding.second; auto type1 = types.Type1; auto type2 = types.Type2; // If either of the types still contains type variables, we can't // compare them. // FIXME: This is really unfortunate. More type variable sharing // (when it's sound) would help us do much better here. if (type1->hasTypeVariable() || type2->hasTypeVariable()) { identical = false; continue; } // With introduction of holes it's currently possible to form solutions // with UnresolvedType bindings, we need to account for that in // ranking. If one solution has a hole for a given type variable // it's always worse than any non-hole type other solution might have. if (type1->is() || type2->is()) { if (type1->is()) { ++score2; } else { ++score1; } identical = false; continue; } // If one type is a subtype of the other, but not vice-versa, // we prefer the system with the more-constrained type. // FIXME: Collapse this check into the second check. auto type1Better = TypeChecker::isSubtypeOf(type1, type2, cs.DC); auto type2Better = TypeChecker::isSubtypeOf(type2, type1, cs.DC); if (type1Better || type2Better) { if (type1Better) ++score1; if (type2Better) ++score2; // Prefer the unlabeled form of a type. auto unlabeled1 = getUnlabeledType(type1, cs.getASTContext()); auto unlabeled2 = getUnlabeledType(type2, cs.getASTContext()); if (unlabeled1->isEqual(unlabeled2)) { if (type1->isEqual(unlabeled1) && !types.Type1WasLabeled) { ++score1; continue; } if (type2->isEqual(unlabeled2) && !types.Type2WasLabeled) { ++score2; continue; } } identical = false; continue; } // The systems are not considered equivalent. identical = false; // Archetypes are worse than concrete types (i.e. non-placeholder and // non-archetype) // FIXME: Total hack. if (type1->is() && !type2->is() && !type2->is()) { ++score2; continue; } else if (type2->is() && !type1->is() && !type1->is()) { ++score1; continue; } // FIXME: // This terrible hack is in place to support equality comparisons of non- // equatable option types to 'nil'. Until we have a way to constrain a type // variable on "!Equatable", if all other aspects of the overload choices // are equal, favor the overload that does not require an implicit literal // argument conversion to 'nil'. // Post-1.0, we'll need to remove this hack in favor of richer constraint // declarations. if (!(score1 || score2)) { if (auto nominalType2 = type2->getNominalOrBoundGenericNominal()) { if ((nominalType2->getName() == cs.getASTContext().Id_OptionalNilComparisonType)) { ++score2; } } if (auto nominalType1 = type1->getNominalOrBoundGenericNominal()) { if ((nominalType1->getName() == cs.getASTContext().Id_OptionalNilComparisonType)) { ++score1; } } } } ୭͔ಡΜͰڭ͍͑ͯͩ͘͞ɻ

Slide 8

Slide 8 text

ղͷൺֱ w 4DPSF͕খ͍͞ํ͕༏ઌ w δΣωϦοΫγάωνϟͷαϒηοτΛ༏ઌ

Slide 9

Slide 9 text

δΣωϦοΫγάωνϟͷαϒηοτ protocol P1 {} protocol P2 {} extension Int: P1 {} extension Int: P2 {} func foo(_ x: T) where T: P1 { print("x conforms P1") } func foo(_ x: T) where T: P1, T: P2 { print("x conforms P1 and P2") } foo(1)

Slide 10

Slide 10 text

׆༻ྫ protocol CustomDatabaseConvertible { associatedtype DatabaseColumnType } protocol Database { // JSONΧϥϜܕͰอଘ͢Δ func save(_ value: T) async throws // ࢦఆͨ͠ΧϥϜܕͰอଘ͢Δ func save(_ value: T) async throws } struct YMD: Codable & CustomDatabaseConvertible { typealias DatabaseColumnType = DateTimeType var year: Int var month: Int var day: Int } w ༏ઌ౓Λ͚͍ͭͨ

Slide 11

Slide 11 text

extension Database { func save(_ value: T) async throws { func narrow(_ value: some Encodable) async throws { try await self.save(value) } try await narrow(value) } } w ྆ํͷαϒηοτΛ࡞͔ͬͯΒखಈͰσΟεύον͠௚͢

Slide 12

Slide 12 text

ܕਪ࿦രൃ func f() -> Bool func f() -> Int func f() -> Float func f() -> Double func f() -> UInt8 func f() -> UInt16 func f() -> UInt32 func f() -> UInt64 func main() { let a = (f(), f(), f(), f(), f(), f()) } code.swift:11:9: error: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions

Slide 13

Slide 13 text

func main() { let a = (f(), f(), f(), f(), f(), f()) } Y Y Y Y Y Y w ສ௨Γͷൺֱ

Slide 14

Slide 14 text

਎ۙͳྫ func main() { let a: Double = (1 + 1) + (1 + 1) + (1 + 1) } op.swift:2:9: error: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions

Slide 15

Slide 15 text

Ϧςϥϧͷܕͷީิ func main() { let a: Double = (1 + 1) + (1 + 1) + (1 + 1) } Double Float Float16 Float80 Int Int16 Int32 Int64 Int8 StaticBigInt UInt UInt16 UInt32 UInt64 UInt8 Y

Slide 16

Slide 16 text

ԋࢉࢠͷީิ 4XJGU*OUFYUFOTJPO *OU5ZQF *OU *OU *OU 4XJGU4USJOHFYUFOTJPO 4USJOH5ZQF 4USJOH 4USJOH 4USJOH 4XJGU3BOHF3FQMBDFBCMF$PMMFDUJPOFYUFOTJPO4FMG 0UIFSXIFSF4FMG3BOHF3FQMBDFBCMF$PMMFDUJPO 0UIFS3BOHF3FQMBDFBCMF$PMMFDUJPO 4FMG&MFNFOUFMG 0UIFS 4FMG 4XJGU'MPBUFYUFOTJPO 'MPBU5ZQF 'MPBU 'MPBU 'MPBU 4XJGU%PVCMFFYUFOTJPO %PVCMF5ZQF %PVCMF %PVCMF %PVCMF 4XJGU6*OUFYUFOTJPO 6*OU5ZQF 6*OU 6*OU 6*OU 4XJGU*OUFYUFOTJPO *OU5ZQF *OU *OU *OU 4XJGU6*OUFYUFOTJPO 6*OU5ZQF 6*OU 6*OU 6*OU 4XJGU*OUFYUFOTJPO *OU5ZQF *OU *OU *OU 4XJGU6*OUFYUFOTJPO 6*OU5ZQF 6*OU 6*OU 6*OU 4XJGU*OUFYUFOTJPO *OU5ZQF *OU *OU *OU 4XJGU6*OUFYUFOTJPO 6*OU5ZQF 6*OU 6*OU 6*OU 4XJGU*OUFYUFOTJPO *OU5ZQF *OU *OU *OU 4XJGU6*OUFYUFOTJPO 6*OU5ZQF 6*OU 6*OU 6*OU 4XJGU%VSBUJPOFYUFOTJPO %VSBUJPO5ZQF %VSBUJPO %VSBUJPO %VSBUJPO @$PODVSSFODZ fi MF *OTUBOUFYUFOTJPO $POUJOVPVT$MPDL*OTUBOU5ZQF $POUJOVPVT$MPDL*OTUBOU %VSBUJPO $POUJOVPVT$MPDL*OTUBOU @$PODVSSFODZ fi MF *OTUBOUFYUFOTJPO 4VTQFOEJOH$MPDL*OTUBOU5ZQF 4VTQFOEJOH$MPDL*OTUBOU %VSBUJPO 4VTQFOEJOH$MPDL*OTUBOU 4XJGU"SSBZFYUFOTJPO&MFNFOU "SSBZ&MFNFOU5ZQF "SSBZ&MFNFOU "SSBZ&MFNFOU "SSBZ&MFNFOU 4XJGU'MPBUJOH1PJOU4FMGXIFSF4FMG'MPBUJOH1PJOU 4FMG5ZQF 4FMG 4FMG 4FMG 4XJGU"EEJUJWF"SJUINFUJD4FMGXIFSF4FMG"EEJUJWF"SJUINFUJD 4FMG5ZQF 4FMG 4FMG 4FMG 4XJGU#JOBSZ*OUFHFS4FMGXIFSF4FMG#JOBSZ*OUFHFS 4FMG5ZQF 4FMG 4FMG 4FMG 4XJGU4USJEFBCMFFYUFOTJPO4FMGXIFSF4FMG@1PJOUFS 4FMG5ZQF 4FMG 4FMG4USJEF 4FMG 4XJGU4USJEFBCMFFYUFOTJPO4FMGXIFSF4FMG@1PJOUFS 4FMG5ZQF 4FMG4USJEF 4FMG 4FMG 4XJGU4FRVFODFFYUFOTJPO4FMGXIFSF4FMG4FRVFODF 4FMG&MFNFOU4USJOH 4FMG5ZQF 4FMG 4USJOH /FWFS 4XJGU4FRVFODFFYUFOTJPO4FMGXIFSF4FMG4FRVFODF 4FMG&MFNFOU4USJOH 4FMG5ZQF 4USJOH 4FMG /FWFS 4XJGU4*.%FYUFOTJPO4FMGXIFSF4FMG4*.% 4FMG4DBMBS'MPBUJOH1PJOU 4FMG5ZQF 4FMG 4FMG 4FMG 4XJGU4*.%FYUFOTJPO4FMGXIFSF4FMG4*.% 4FMG4DBMBS'JYFE8JEUI*OUFHFS 4FMG5ZQF 4FMG 4FMG 4FMG 4XJGU4*.%FYUFOTJPO4FMGXIFSF4FMG4*.% 4FMG4DBMBS'JYFE8JEUI*OUFHFS 4FMG5ZQF 4FMG 4FMG4DBMBS 4FMG 4XJGU4*.%FYUFOTJPO4FMGXIFSF4FMG4*.% 4FMG4DBMBS'JYFE8JEUI*OUFHFS 4FMG5ZQF 4FMG4DBMBS 4FMG 4FMG 4XJGU4*.%FYUFOTJPO4FMGXIFSF4FMG4*.% 4FMG4DBMBS'MPBUJOH1PJOU 4FMG5ZQF 4FMG4DBMBS 4FMG 4FMG 4XJGU4*.%FYUFOTJPO4FMGXIFSF4FMG4*.% 4FMG4DBMBS'MPBUJOH1PJOU 4FMG5ZQF 4FMG 4FMG4DBMBS 4FMG 4XJGU3BOHF3FQMBDFBCMF$PMMFDUJPOFYUFOTJPO4FMG 0UIFSXIFSF4FMG3BOHF3FQMBDFBCMF$PMMFDUJPO 0UIFS4FRVFODF 4FMG&MFNFOU0UIFS&MFNFOU4FMG 4XJGU3BOHF3FQMBDFBCMF$PMMFDUJPOFYUFOTJPO4FMG 0UIFSXIFSF4FMG3BOHF3FQMBDFBCMF$PMMFDUJPO 0UIFS4FRVFODF 4FMG&MFNFOU0UIFS&MFNFOU4FMG 4XJGU'MPBUFYUFOTJPO 'MPBU5ZQF 'MPBU 'MPBU 'MPBU Y func main() { let a: Double = (1 + 1) + (1 + 1) + (1 + 1) }

Slide 17

Slide 17 text

ࣜͷܗঢ়ͱ୳ࡧઓུ func main0() { // error let a: Double = (1 + 1) + (1 + 1) + (1 + 1) } func main1() { // ok let a: Double = 1 + 1 + 1 + 1 + 1 + 1 } w Χοίͷ༗ແͰԿ͕มΘ͍ͬͯΔ͔ʁ

Slide 18

Slide 18 text

let a: Double = 1 + 2 + 3 + 4 + = a + + 1 2 3 4 Double x34 ͜͜"45͕ؒҧͬͯ·ͨ͠ɻ͜Εͩͱӈ݁߹͚ͩͲɺਖ਼͘͠͸ࠨ݁߹ɻͨͩ͠಺༰ʹ͸ෆ౎߹ແ͍Ͱ͢ɻ

Slide 19

Slide 19 text

+ = a + + 1 2 3 4 Double Int8.+ x34 ࠷ऴతʹؒҧ͏ީิΛϐοΫͨ͠৔߹

Slide 20

Slide 20 text

+ = a + + 1 2 3 4 Double Int8.+ Double.+ T.+ … ͏·͍͘͘ Int.+ UInt8.+ Int8.+ Τϥʔ Τϥʔ Τϥʔ ௨Δ͕ෛ͚Δ ଞಉ༷

Slide 21

Slide 21 text

+ = a + + 1 2 3 4 Double Int8.+ Int8.+ Int8.+ x34 x34 x34 ॳखͷࣗ༝બ୒ ͭʹߜΒΕΔ ͭʹߜΒΕΔ w º w ৳ͼͨ෦෼͕଍͠ࢉͳͷͰരൃ͠ͳ͍ w ֻ͚ࢉͩͱരൃ͢Δ

Slide 22

Slide 22 text

let a: Double = (1 + 2) + (3 + 4) + (5 + 6) + = a + + + 5 6 Double 1 2 + 3 4

Slide 23

Slide 23 text

+ = a + + + 5 6 Double 1 2 + 3 4 Int8.+ Int8.+ ॳखͷࣗ༝બ୒ ࣍ͷ͕͜͜ߜΕͳ͍ w ࣍ͷީิΛબͿͱ͖ʹɺͦΕ͕ઌߦ͢Δ աఔͱಠཱͯ͠ΔͱߜΓࠐΊͣɺֻ͚ࢉ Ͱ߹੒͞Εͯ͠·͏ 🙇͜ͷ୳ࡧγφϦΦ͸ԾઆͰ͢ɻ୭͔ղੳͯ͠ڭ͍͑ͯͩ͘͞ɻ

Slide 24

Slide 24 text

Ϧςϥϧͷਪ࿦ w Ϧςϥϧͷܕͷਪ࿦Ͱ͸ɺ४ڌ͢Δܕશͯͷީิੜ੒͸͞Εͳ͍ w Φʔόʔϩʔυͷਪ࿦ͱ͸ҧ͏ڍಈΛ͢Δ

Slide 25

Slide 25 text

protocol P {} struct S: P & ExpressibleByIntegerLiteral { typealias IntegerLiteralType = Int init(integerLiteral value: Int) {} } func foo(_ x: some P) {} foo(1) a.swift:11:1: error: global function 'foo' requires that 'Int' conform to 'P' foo(1) ^ w 4͸ީิੜ੒͞Εͳ͍

Slide 26

Slide 26 text

func foo(_ x: some P) {} func foo(_ x: S) {} foo(1) w ͜͏͢ΔͱɺΦʔόʔϩʔυͷީิ͔Β4͕ࢼߦ͞ΕΔ

Slide 27

Slide 27 text

Ϧςϥϧͷީิੜ੒ w ಛผʹܾΊΒΕͨܕ͚͕ͩީิͱͯ͠ੜ੒͞ΕΔ w ੔਺Ϧςϥϧ→ Int, Double w খ਺Ϧςϥϧ→ Double w จࣈྻϦςϥϧ"Swift" → String

Slide 28

Slide 28 text

extension Double: P {} func foo(_ x: some P) {} foo(1) w %PVCMF͸ީิʹग़ͯ͘ΔͷͰ௨Δ

Slide 29

Slide 29 text

ෳ਺จΫϩʔδϟͷਪ࿦

Slide 30

Slide 30 text

struct S {} struct K {} func run(_ body: () -> R) -> R { body() } func foo(_ x: S) -> S { x } func foo(_ x: K) -> K { x } let x = run { () in let a = S() return foo(a) } w GPP͕୒͋Δ͕ɺBͷܕ͔ΒΘ͔Δ w ΍ͬͨ͋؆୯ͩ͋😀ˠͰ͸ͳ͍🙅

Slide 31

Slide 31 text

෦෼໰୊ͱґଘؔ܎ let x = run { () in let a = S() return foo(a) } let x = run { () in let a = S() return foo(a) } ෦෼໰୊ ෦෼໰୊ ґଘํ޲ w ෦෼໰୊͸ɺ෦෼໰୊ͷ݁Ռ Bͷܕ ΛࢀরͰ͖Δ w ٯ͸Ͱ͖ͳ͍

Slide 32

Slide 32 text

let x: S = run { () in let a = .init() return foo(a) } a.swift:12:14: error: reference to member 'init' cannot be resolved without a contextual type let a = .init() ~^~~~ w BΛղ࣌͘ʹGPPʹ౉͍ͯ͠ΔࣄΛߟྀͰ͖ͳ͍

Slide 33

Slide 33 text

let x = run { () in if c { return S() } return .init() } Ϋϩʔδϟࣜͷൣғ 0, let x = run { () in if c { return .init() } return S() } a.swift:15:17: error: cannot infer contextual base in reference to member 'init' return .init() ~^~~~

Slide 34

Slide 34 text

let x = run { () in if c { return .init() } return S() } let x = run { () in if c { return .init() } return S() } ෦෼໰୊ ෦෼໰୊ w ࠷ॳͷSFUVSO͕Ϋϩʔδϟͷܕͷਪ࿦ʹ૊Έࠐ·ΕΔ w ଞͷSFUVSO͸ҰகΛνΣοΫ͞ΕΔ͚ͩ ᐆດͰղ͚ͳ͍

Slide 35

Slide 35 text

let x = run { () in if c { return S() } return Optional(S()) } a.swift:18:12: error: value of optional type 'S?' must be unwrapped to a value of type 'S' return Optional(S()) ^ w Ϋϩʔδϟ͸() -> SͰ֬ఆͯ͠͠·͏ͷͰɺޙΖͷ Optional͸Τϥʔʹͳͬͯ͠·͏ɻ

Slide 36

Slide 36 text

JGࣜͷਪ࿦

Slide 37

Slide 37 text

let x = if c { 1 } else { 2 } w JGࣜͷܕ͕UIFO FMTF͔Βਪ࿦͞ΕΔ w ΍ͬͨ͋؆୯ͩ͋😀ˠͰ͸ͳ͍🙅

Slide 38

Slide 38 text

let x = if c { 1 } else { 2.0 } a.swift:6:5: error: branches have mismatching types 'Int' and 'Double' 1 ^ Double( ) w UIFOͱFMTF͸ಠཱʹਪ࿦͞ΕΔ w ͦͷޙͰ౷߹͞ΕΔ

Slide 39

Slide 39 text

શһ౿Ή΍ͭ let x = if c { S() } else { nil } a.swift:8:5: error: 'nil' requires a contextual type nil ^ w FMTFΛղ࣌͘ʹͳΜͷOJM͔Θ͔Βͳ͍

Slide 40

Slide 40 text

let x: S? = if c { S() } else { nil } ରࡦ let x = if c { S() } else { S?.none } let x = if c { S() } else { nil as S? } ࠨลͷܕΛ༩͑Δ FMTFͰ໌ࣔతʹॻ͘ FMTFͰ໌ࣔతʹॻ͘ w ࠨลͷܕ͸UIFOͱFMTFΛղ͘ͱ͖ʹࢀর͞ΕΔɻ w Կ͔͠Βॻ͚͹௨Δɾɾɾ😞

Slide 41

Slide 41 text

ࡾ߲ԋࢉࢠ let x = c ? S() : nil w ࡾ߲ԋࢉࢠ͸໰୊ͷ෼ׂΛ͠ͳ͍ͷͰͦͷ··ղ͚Δ

Slide 42

Slide 42 text

It differs from the behavior of the ternary operator (let x = p ? 0 : 1.0 compiles, with x: Double). However, the impact of bidirectional inference on the performance of the type checker would likely prohibit this feature from being implemented today, even if it were considered preferable. This is especially true in cases where there are many branches. This decision could be revisited in future: switching to full bidirectional type inference may be source breaking in theory, but probably not in practice (the proposal authors can't think of any examples where it would be). ࡾ߲ԋࢉࢠʢlet x = p ? 0 : 1.0͸ίϯύΠϧ͞Εɺx: DoubleͱͳΔʣͷಈ࡞ͱ͸ҟͳΓ·͢ɻ ͔͠͠ɺ૒ํ޲ܕਪ࿦͕ܕνΣοΧʔͷύϑΥʔϚϯεʹ༩͑ΔӨڹΛߟ͑Δͱɺͨͱ͑͜ͷػೳ͕๬·͍͠ͱߟ ͑ΒΕͯ΋ɺݱ࣌఺Ͱ͸࣮૷͢Δͷ͸೉͍͠Ͱ͠ΐ͏ɻಛʹ෼ذ͕ଟ͍৔߹ʹͦͷӨڹ͸ݦஶͰ͢ɻ͜ͷܾఆ͸ কདྷతʹ࠶ݕ౼͞ΕΔՄೳੑ͕͋Γ·͢ɻ׬શͳ૒ํ޲ܕਪ࿦ʹ੾Γସ͑Δ͜ͱ͸ཧ࿦తʹ͸ιʔείʔυΛյ ͢Մೳੑ͕͋Γ·͕͢ɺ࣮ࡍʹ͸ͦ͏ͳΒͳ͍Ͱ͠ΐ͏ʢఏҊऀ͸ͦͷΑ͏ͳྫΛࢥ͍͖ͭ·ͤΜʣɻ ݪจ4&