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

型推論のちょっと深い話

 型推論のちょっと深い話

わいわいswiftc#41

omochimetaru

May 30, 2024
Tweet

More Decks by omochimetaru

Other Decks in Programming

Transcript

  1. Φʔόʔϩʔυબ୒ 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
  2. ղͷൺֱ 4PMVUJPO$PNQBSF3FTVMU$POTUSBJOU4ZTUFNDPNQBSF4PMVUJPOT  $POTUSBJOU4ZTUFNDT "SSBZ3FG4PMVUJPOTPMVUJPOT  DPOTU4PMVUJPO%J ff EJ ff

    VOTJHOFEJEY VOTJHOFEJEY 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; }
  3. // 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();
  4. // 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; } } } } ୭͔ಡΜͰڭ͍͑ͯͩ͘͞ɻ
  5. δΣωϦοΫγάωνϟͷαϒηοτ 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)
  6. ׆༻ྫ 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 ༏ઌ౓Λ͚͍ͭͨ
  7. 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 ྆ํͷαϒηοτΛ࡞͔ͬͯΒखಈͰσΟεύον͠௚͢
  8. ܕਪ࿦രൃ 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
  9. func main() { let a = (f(), f(), f(), f(),

    f(), f()) } Y Y Y Y Y Y w ສ௨Γͷൺֱ
  10. ਎ۙͳྫ 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
  11. Ϧςϥϧͷܕͷީิ 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
  12. ԋࢉࢠͷީิ 4XJGU*OUFYUFOTJPO  *OU5ZQF  *OU *OU *OU 4XJGU4USJOHFYUFOTJPO 

    4USJOH5ZQF  4USJOH 4USJOH 4USJOH 4XJGU3BOHF3FQMBDFBCMF$PMMFDUJPOFYUFOTJPO 4FMG 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'MPBUJOH1PJOU 4FMGXIFSF4FMG'MPBUJOH1PJOU 4FMG5ZQF  4FMG 4FMG 4FMG 4XJGU"EEJUJWF"SJUINFUJD 4FMGXIFSF4FMG"EEJUJWF"SJUINFUJD 4FMG5ZQF  4FMG 4FMG 4FMG 4XJGU#JOBSZ*OUFHFS 4FMGXIFSF4FMG#JOBSZ*OUFHFS 4FMG5ZQF  4FMG 4FMG 4FMG 4XJGU4USJEFBCMFFYUFOTJPO 4FMGXIFSF4FMG@1PJOUFS 4FMG5ZQF  4FMG 4FMG4USJEF 4FMG 4XJGU4USJEFBCMFFYUFOTJPO 4FMGXIFSF4FMG@1PJOUFS 4FMG5ZQF  4FMG4USJEF 4FMG 4FMG 4XJGU4FRVFODFFYUFOTJPO 4FMGXIFSF4FMG4FRVFODF 4FMG&MFNFOU4USJOH 4FMG5ZQF  4FMG 4USJOH /FWFS 4XJGU4FRVFODFFYUFOTJPO 4FMGXIFSF4FMG4FRVFODF 4FMG&MFNFOU4USJOH 4FMG5ZQF  4USJOH 4FMG /FWFS 4XJGU4*.%FYUFOTJPO 4FMGXIFSF4FMG4*.% 4FMG4DBMBS'MPBUJOH1PJOU 4FMG5ZQF  4FMG 4FMG 4FMG 4XJGU4*.%FYUFOTJPO 4FMGXIFSF4FMG4*.% 4FMG4DBMBS'JYFE8JEUI*OUFHFS 4FMG5ZQF  4FMG 4FMG 4FMG 4XJGU4*.%FYUFOTJPO 4FMGXIFSF4FMG4*.% 4FMG4DBMBS'JYFE8JEUI*OUFHFS 4FMG5ZQF  4FMG 4FMG4DBMBS 4FMG 4XJGU4*.%FYUFOTJPO 4FMGXIFSF4FMG4*.% 4FMG4DBMBS'JYFE8JEUI*OUFHFS 4FMG5ZQF  4FMG4DBMBS 4FMG 4FMG 4XJGU4*.%FYUFOTJPO 4FMGXIFSF4FMG4*.% 4FMG4DBMBS'MPBUJOH1PJOU 4FMG5ZQF  4FMG4DBMBS 4FMG 4FMG 4XJGU4*.%FYUFOTJPO 4FMGXIFSF4FMG4*.% 4FMG4DBMBS'MPBUJOH1PJOU 4FMG5ZQF  4FMG 4FMG4DBMBS 4FMG 4XJGU3BOHF3FQMBDFBCMF$PMMFDUJPOFYUFOTJPO 4FMG 0UIFSXIFSF4FMG3BOHF3FQMBDFBCMF$PMMFDUJPO 0UIFS4FRVFODF 4FMG&MFNFOU0UIFS&MFNFOU4FMG 4XJGU3BOHF3FQMBDFBCMF$PMMFDUJPOFYUFOTJPO 4FMG 0UIFSXIFSF4FMG3BOHF3FQMBDFBCMF$PMMFDUJPO 0UIFS4FRVFODF 4FMG&MFNFOU0UIFS&MFNFOU4FMG 4XJGU'MPBUFYUFOTJPO  'MPBU5ZQF  'MPBU 'MPBU 'MPBU Y func main() { let a: Double = (1 + 1) + (1 + 1) + (1 + 1) }
  13. ࣜͷܗঢ়ͱ୳ࡧઓུ 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 Χοίͷ༗ແͰԿ͕มΘ͍ͬͯΔ͔ʁ
  14. let a: Double = 1 + 2 + 3 +

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

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

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

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

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

    2 + 3 4 Int8.+ Int8.+ ॳखͷࣗ༝બ୒ ࣍ͷ͕͜͜ߜΕͳ͍ w ࣍ͷީิΛબͿͱ͖ʹɺͦΕ͕ઌߦ͢Δ աఔͱಠཱͯ͠ΔͱߜΓࠐΊͣɺֻ͚ࢉ Ͱ߹੒͞Εͯ͠·͏ 🙇͜ͷ୳ࡧγφϦΦ͸ԾઆͰ͢ɻ୭͔ղੳͯ͠ڭ͍͑ͯͩ͘͞ɻ
  20. 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͸ީิੜ੒͞Εͳ͍
  21. func foo(_ x: some P) {} func foo(_ x: S)

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

    foo(1) w %PVCMF͸ީิʹग़ͯ͘ΔͷͰ௨Δ
  23. 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 ΍ͬͨ͋؆୯ͩ͋😀ˠͰ͸ͳ͍🙅
  24. ෦෼໰୊ͱґଘؔ܎ let x = run { () in let a

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

    return .init() } return S() } let x = run { () in if c { return .init() } return S() } ෦෼໰୊ ෦෼໰୊ w ࠷ॳͷSFUVSO͕Ϋϩʔδϟͷܕͷਪ࿦ʹ૊Έࠐ·ΕΔ w ଞͷSFUVSO͸ҰகΛνΣοΫ͞ΕΔ͚ͩ ᐆດͰղ͚ͳ͍
  28. 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>͸Τϥʔʹͳͬͯ͠·͏ɻ
  29. let x = if c { 1 } else {

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

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

    ࡾ߲ԋࢉࢠ͸໰୊ͷ෼ׂΛ͠ͳ͍ͷͰͦͷ··ղ͚Δ
  34. 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&