omochimetaru
May 30, 2024
330

型推論のちょっと深い話

わいわいswiftc#41

May 30, 2024

Transcript

3. ࠓ೔ͷ࿩ w Φʔόʔϩʔυબ୒ɺδΣωϦοΫγάωνϟแؚؔ܎ w ܕਪ࿦രൃ w Ϧςϥϧͷਪ࿦ w ෳ਺จΫϩʔδϟͷਪ࿦ w

JGࣜͷਪ࿦ w ܾఆෆೳ໰୊
4. Φʔόʔϩʔυબ୒ 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
5. ղͷൺֱ 4PMVUJPO\$PNQBSF3FTVMU\$POTUSBJOU4ZTUFNDPNQBSF4PMVUJPOT  \$POTUSBJOU4ZTUFNDT "SSBZ3FG4PMVUJPOTPMVUJPOT  DPOTU4PMVUJPO%J ff EJ ff

VOTJHOFEJEY VOTJHOFEJEY SolutionCompareResult ConstraintSystem::compareSolutions( ConstraintSystem &cs, ArrayRef<Solution> 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<Expr *>()) { auto weight = cs.getExprDepth(anchor); if (weight) return *weight + 1; } return 1; }; SmallVector<SolutionDiff::OverloadDiff, 4> 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<T>` // or `ArraySlice<T>` (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<ApplyExpr>(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<ProtocolDecl>(dc1); auto pd2 = dyn_cast<ProtocolDecl>(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; }
6. // 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<ConstructorDecl>(decl1)) { if (auto ctor2 = dyn_cast<ConstructorDecl>(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<TypeDecl>(decl1) && isa<AbstractFunctionDecl>(decl2)) || (isa<AbstractFunctionDecl>(decl1) && isa<TypeDecl>(decl2)))) { if (isa<TypeDecl>(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<VarDecl>(decl1) && isa<FuncDecl>(decl2)) score1 += weight; else if (isa<VarDecl>(decl2) && isa<FuncDecl>(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<VarDecl>(decl1) && isa<VarDecl>(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<VarDecl>(decl1) && isa<VarDecl>(decl2)) { auto *nominal1 = dc1->getSelfNominalTypeDecl(); auto *nominal2 = dc2->getSelfNominalTypeDecl(); if (nominal1 && nominal2 && nominal1 != nominal2) { isVarAndNotProtocol1 = !isa<ProtocolDecl>(nominal1); isVarAndNotProtocol2 = !isa<ProtocolDecl>(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<AnyFunctionType>(); 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<AnyFunctionType>(); 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<TypeVariableType *, TypeBindingsToCompare> 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();
7. // 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<LocatorPathElt::ConstructorMemberType>()) { 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<UnresolvedType>() || type2->is<UnresolvedType>()) { if (type1->is<UnresolvedType>()) { ++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<ArchetypeType>() && !type2->is<ArchetypeType>() && !type2->is<PlaceholderType>()) { ++score2; continue; } else if (type2->is<ArchetypeType>() && !type1->is<ArchetypeType>() && !type1->is<PlaceholderType>()) { ++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; } } } } ୭͔ಡΜͰڭ͍͑ͯͩ͘͞ɻ

9. δΣωϦοΫγάωνϟͷαϒηοτ protocol P1 {} protocol P2 {} extension Int: P1

{} extension Int: P2 {} func foo<T>(_ x: T) where T: P1 { print("x conforms P1") } func foo<T>(_ x: T) where T: P1, T: P2 { print("x conforms P1 and P2") } foo(1)
10. ׆༻ྫ protocol CustomDatabaseConvertible { associatedtype DatabaseColumnType } protocol Database {

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

async throws { func narrow(_ value: some Encodable) async throws { try await self.save(value) } try await narrow(value) } } w ྆ํͷαϒηοτΛ࡞͔ͬͯΒखಈͰσΟεύον͠௚͢
12. ܕਪ࿦രൃ 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
13. func main() { let a = (f(), f(), f(), f(),

f(), f()) } Y Y Y Y Y Y w ສ௨Γͷൺֱ
14. ਎ۙͳྫ 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
15. Ϧςϥϧͷܕͷީิ 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
16. ԋࢉࢠͷީิ 4XJGU*OUFYUFOTJPO  *OU5ZQF  *OU *OU *OU 4XJGU4USJOHFYUFOTJPO 

4USJOH5ZQF  4USJOH 4USJOH 4USJOH 4XJGU3BOHF3FQMBDFBCMF\$PMMFDUJPOFYUFOTJPO 4FMG 0UIFSXIFSF4FMG3BOHF3FQMBDFBCMF\$PMMFDUJPO 0UIFS3BOHF3FQMBDFBCMF\$PMMFDUJPO 4FMG&MFNFOUFMG 0UIFS 4FMG 4XJGU'MPBUFYUFOTJPO  'MPBU5ZQF  'MPBU 'MPBU 'MPBU 4XJGU%PVCMFFYUFOTJPO  %PVCMF5ZQF  %PVCMF %PVCMF %PVCMF 4XJGU6*OUFYUFOTJPO  6*OU5ZQF  6*OU 6*OU 6*OU 4XJGU*OUFYUFOTJPO  *OU5ZQF  *OU *OU *OU 4XJGU6*OUFYUFOTJPO  6*OU5ZQF  6*OU 6*OU 6*OU 4XJGU*OUFYUFOTJPO  *OU5ZQF  *OU *OU *OU 4XJGU6*OUFYUFOTJPO  6*OU5ZQF  6*OU 6*OU 6*OU 4XJGU*OUFYUFOTJPO  *OU5ZQF  *OU *OU *OU 4XJGU6*OUFYUFOTJPO  6*OU5ZQF  6*OU 6*OU 6*OU 4XJGU*OUFYUFOTJPO  *OU5ZQF  *OU *OU *OU 4XJGU6*OUFYUFOTJPO  6*OU5ZQF  6*OU 6*OU 6*OU 4XJGU%VSBUJPOFYUFOTJPO  %VSBUJPO5ZQF  %VSBUJPO %VSBUJPO %VSBUJPO @\$PODVSSFODZ fi MF *OTUBOUFYUFOTJPO  \$POUJOVPVT\$MPDL*OTUBOU5ZQF  \$POUJOVPVT\$MPDL*OTUBOU %VSBUJPO \$POUJOVPVT\$MPDL*OTUBOU @\$PODVSSFODZ fi MF *OTUBOUFYUFOTJPO  4VTQFOEJOH\$MPDL*OTUBOU5ZQF  4VTQFOEJOH\$MPDL*OTUBOU %VSBUJPO 4VTQFOEJOH\$MPDL*OTUBOU 4XJGU"SSBZFYUFOTJPO &MFNFOU "SSBZ&MFNFOU5ZQF  "SSBZ&MFNFOU "SSBZ&MFNFOU "SSBZ&MFNFOU 4XJGU'MPBUJOH1PJOU 4FMGXIFSF4FMG'MPBUJOH1PJOU 4FMG5ZQF  4FMG 4FMG 4FMG 4XJGU"EEJUJWF"SJUINFUJD 4FMGXIFSF4FMG"EEJUJWF"SJUINFUJD 4FMG5ZQF  4FMG 4FMG 4FMG 4XJGU#JOBSZ*OUFHFS 4FMGXIFSF4FMG#JOBSZ*OUFHFS 4FMG5ZQF  4FMG 4FMG 4FMG 4XJGU4USJEFBCMFFYUFOTJPO 4FMGXIFSF4FMG@1PJOUFS 4FMG5ZQF  4FMG 4FMG4USJEF 4FMG 4XJGU4USJEFBCMFFYUFOTJPO 4FMGXIFSF4FMG@1PJOUFS 4FMG5ZQF  4FMG4USJEF 4FMG 4FMG 4XJGU4FRVFODFFYUFOTJPO 4FMGXIFSF4FMG4FRVFODF 4FMG&MFNFOU4USJOH 4FMG5ZQF  4FMG 4USJOH /FWFS 4XJGU4FRVFODFFYUFOTJPO 4FMGXIFSF4FMG4FRVFODF 4FMG&MFNFOU4USJOH 4FMG5ZQF  4USJOH 4FMG /FWFS 4XJGU4*.%FYUFOTJPO 4FMGXIFSF4FMG4*.% 4FMG4DBMBS'MPBUJOH1PJOU 4FMG5ZQF  4FMG 4FMG 4FMG 4XJGU4*.%FYUFOTJPO 4FMGXIFSF4FMG4*.% 4FMG4DBMBS'JYFE8JEUI*OUFHFS 4FMG5ZQF  4FMG 4FMG 4FMG 4XJGU4*.%FYUFOTJPO 4FMGXIFSF4FMG4*.% 4FMG4DBMBS'JYFE8JEUI*OUFHFS 4FMG5ZQF  4FMG 4FMG4DBMBS 4FMG 4XJGU4*.%FYUFOTJPO 4FMGXIFSF4FMG4*.% 4FMG4DBMBS'JYFE8JEUI*OUFHFS 4FMG5ZQF  4FMG4DBMBS 4FMG 4FMG 4XJGU4*.%FYUFOTJPO 4FMGXIFSF4FMG4*.% 4FMG4DBMBS'MPBUJOH1PJOU 4FMG5ZQF  4FMG4DBMBS 4FMG 4FMG 4XJGU4*.%FYUFOTJPO 4FMGXIFSF4FMG4*.% 4FMG4DBMBS'MPBUJOH1PJOU 4FMG5ZQF  4FMG 4FMG4DBMBS 4FMG 4XJGU3BOHF3FQMBDFBCMF\$PMMFDUJPOFYUFOTJPO 4FMG 0UIFSXIFSF4FMG3BOHF3FQMBDFBCMF\$PMMFDUJPO 0UIFS4FRVFODF 4FMG&MFNFOU0UIFS&MFNFOU4FMG 4XJGU3BOHF3FQMBDFBCMF\$PMMFDUJPOFYUFOTJPO 4FMG 0UIFSXIFSF4FMG3BOHF3FQMBDFBCMF\$PMMFDUJPO 0UIFS4FRVFODF 4FMG&MFNFOU0UIFS&MFNFOU4FMG 4XJGU'MPBUFYUFOTJPO  'MPBU5ZQF  'MPBU 'MPBU 'MPBU Y func main() { let a: Double = (1 + 1) + (1 + 1) + (1 + 1) }
17. ࣜͷܗঢ়ͱ୳ࡧઓུ 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 Χοίͷ༗ແͰԿ͕มΘ͍ͬͯΔ͔ʁ
18. let a: Double = 1 + 2 + 3 +

4 + = a + + 1 2 3 4 Double x34 ͜͜"45͕ؒҧͬͯ·ͨ͠ɻ͜Εͩͱӈ݁߹͚ͩͲɺਖ਼͘͠͸ࠨ݁߹ɻͨͩ͠಺༰ʹ͸ෆ౎߹ແ͍Ͱ͢ɻ
19. + = a + + 1 2 3 4 Double

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

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

Int8.+ Int8.+ Int8.+ x34 x34 x34 ॳखͷࣗ༝બ୒ ͭʹߜΒΕΔ ͭʹߜΒΕΔ w º    w ৳ͼͨ෦෼͕଍͠ࢉͳͷͰരൃ͠ͳ͍ w ֻ͚ࢉͩͱരൃ͢Δ
22. let a: Double = (1 + 2) + (3 +

4) + (5 + 6) + = a + + + 5 6 Double 1 2 + 3 4
23. + = a + + + 5 6 Double 1

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

25. 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͸ީิੜ੒͞Εͳ͍
26. func foo(_ x: some P) {} func foo(_ x: S)

{} foo(1) w ͜͏͢ΔͱɺΦʔόʔϩʔυͷީิ͔Β4͕ࢼߦ͞ΕΔ
27. Ϧςϥϧͷީิੜ੒ w ಛผʹܾΊΒΕͨܕ͚͕ͩީิͱͯ͠ੜ੒͞ΕΔ w ੔਺Ϧςϥϧ→ Int, Double w খ਺Ϧςϥϧ→ Double

w จࣈྻϦςϥϧ"Swift" → String
28. extension Double: P {} func foo(_ x: some P) {}

foo(1) w %PVCMF͸ީิʹग़ͯ͘ΔͷͰ௨Δ

30. struct S {} struct K {} func run<R>(_ 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 ΍ͬͨ͋؆୯ͩ͋😀ˠͰ͸ͳ͍🙅
31. ෦෼໰୊ͱґଘؔ܎ let x = run { () in let a

= S() return foo(a) } let x = run { () in let a = S() return foo(a) } ෦෼໰୊ ෦෼໰୊ ґଘํ޲ w ෦෼໰୊͸ɺ෦෼໰୊ͷ݁Ռ Bͷܕ ΛࢀরͰ͖Δ w ٯ͸Ͱ͖ͳ͍
32. 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ʹ౉͍ͯ͠ΔࣄΛߟྀͰ͖ͳ͍
33. 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() ~^~~~
34. let x = run { () in if c {

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

return S() } return Optional<S>(S()) } a.swift:18:12: error: value of optional type 'S?' must be unwrapped to a value of type 'S' return Optional<S>(S()) ^ w Ϋϩʔδϟ͸() -> SͰ֬ఆͯ͠͠·͏ͷͰɺޙΖͷ Optional<S>͸Τϥʔʹͳͬͯ͠·͏ɻ

37. let x = if c { 1 } else {

2 } w JGࣜͷܕ͕UIFO FMTF͔Βਪ࿦͞ΕΔ w ΍ͬͨ͋؆୯ͩ͋😀ˠͰ͸ͳ͍🙅
38. 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 ͦͷޙͰ౷߹͞ΕΔ
39. શһ౿Ή΍ͭ let x = if c { S() } else

{ nil } a.swift:8:5: error: 'nil' requires a contextual type nil ^ w FMTFΛղ࣌͘ʹͳΜͷOJM͔Θ͔Βͳ͍
40. 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 Կ͔͠Βॻ͚͹௨Δɾɾɾ😞
41. ࡾ߲ԋࢉࢠ let x = c ? S() : nil w

ࡾ߲ԋࢉࢠ͸໰୊ͷ෼ׂΛ͠ͳ͍ͷͰͦͷ··ղ͚Δ
42. 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&