//===--- CSSimplify.cpp - Constraint Simplification -----------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements simplifications of constraints within the constraint // system. // //===----------------------------------------------------------------------===// #include "CSDiagnostics.h" #include "OpenedExistentials.h" #include "TypeCheckConcurrency.h" #include "TypeCheckEffects.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/ConformanceLookup.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Initializer.h" #include "swift/AST/NameLookupRequests.h" #include "swift/AST/PackExpansionMatcher.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PropertyWrappers.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/Requirement.h" #include "swift/AST/SourceFile.h" #include "swift/AST/Types.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/StringExtras.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/Sema/CSFix.h" #include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/IDETypeChecking.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/Compiler.h" using namespace swift; using namespace constraints; MatchCallArgumentListener::~MatchCallArgumentListener() { } bool MatchCallArgumentListener::extraArgument(unsigned argIdx) { return true; } std::optional MatchCallArgumentListener::missingArgument(unsigned paramIdx, unsigned argInsertIdx) { return std::nullopt; } bool MatchCallArgumentListener::missingLabel(unsigned paramIdx) { return true; } bool MatchCallArgumentListener::extraneousLabel(unsigned paramIdx) { return true; } bool MatchCallArgumentListener::incorrectLabel(unsigned paramIdx) { return true; } bool MatchCallArgumentListener::outOfOrderArgument( unsigned argIdx, unsigned prevArgIdx, ArrayRef bindings) { return true; } bool MatchCallArgumentListener::relabelArguments(ArrayRef newNames){ return true; } bool MatchCallArgumentListener::shouldClaimArgDuringRecovery(unsigned argIdx) { return true; } bool MatchCallArgumentListener::canClaimArgIgnoringNameMismatch( const AnyFunctionType::Param &arg) { return false; } /// Produce a score (smaller is better) comparing a parameter name and /// potentially-typo'd argument name. /// /// \param paramName The name of the parameter. /// \param argName The name of the argument. /// \param maxScore The maximum score permitted by this comparison, or /// 0 if there is no limit. /// /// \returns the score, if it is good enough to even consider this a match. /// Otherwise, an empty optional. /// static std::optional scoreParamAndArgNameTypo(StringRef paramName, StringRef argName, unsigned maxScore) { using namespace camel_case; // Compute the edit distance. unsigned dist = argName.edit_distance(paramName, /*AllowReplacements=*/true, /*MaxEditDistance=*/maxScore); // If the edit distance would be too long, we're done. if (maxScore != 0 && dist > maxScore) return std::nullopt; // The distance can be zero due to the "with" transformation above. if (dist == 0) return 1; // If this is just a single character label on both sides, // simply return distance. if (paramName.size() == 1 && argName.size() == 1) return dist; // Only allow about one typo for every two properly-typed characters, which // prevents completely-wacky suggestions in many cases. if (dist > (argName.size() + 1) / 3) return std::nullopt; return dist; } bool constraints::isPackExpansionType(Type type) { if (type->is()) return true; if (auto *typeVar = type->getAs()) return typeVar->getImpl().isPackExpansion(); return false; } bool constraints::isSingleUnlabeledPackExpansionTuple(Type type) { auto *tuple = type->getRValueType()->getAs(); return tuple && (tuple->getNumElements() == 1) && isPackExpansionType(tuple->getElementType(0)) && !tuple->getElement(0).hasName(); } Type constraints::getPatternTypeOfSingleUnlabeledPackExpansionTuple(Type type) { if (isSingleUnlabeledPackExpansionTuple(type)) { auto tuple = type->getRValueType()->castTo(); const auto &tupleElement = tuple->getElementType(0); if (auto *expansion = tupleElement->getAs()) { return expansion->getPatternType(); } if (auto *typeVar = tupleElement->getAs()) { auto *locator = typeVar->getImpl().getLocator(); if (auto expansionElement = locator->getLastElementAs()) { return expansionElement->getOpenedType()->getPatternType(); } } } return {}; } bool constraints::containsPackExpansionType(ArrayRef params) { return llvm::any_of(params, [&](const auto ¶m) { return isPackExpansionType(param.getPlainType()); }); } bool constraints::containsPackExpansionType(TupleType *tuple) { return llvm::any_of(tuple->getElements(), [&](const auto &elt) { return isPackExpansionType(elt.getType()); }); } bool constraints::doesMemberRefApplyCurriedSelf(Type baseTy, const ValueDecl *decl) { assert(decl->getDeclContext()->isTypeContext() && "Expected a member reference"); // For a reference to an instance method on a metatype, we want to keep the // curried self. if (decl->isInstanceMember()) { assert(baseTy); if (isa(decl) && baseTy->getRValueType()->is()) return false; } // Otherwise the reference applies self. return true; } static bool areConservativelyCompatibleArgumentLabels( ConstraintSystem &cs, OverloadChoice choice, SmallVectorImpl &args, MatchCallArgumentListener &listener, std::optional unlabeledTrailingClosureArgIndex) { ValueDecl *decl = nullptr; switch (choice.getKind()) { case OverloadChoiceKind::Decl: case OverloadChoiceKind::DeclViaBridge: case OverloadChoiceKind::DeclViaDynamic: case OverloadChoiceKind::DeclViaUnwrappedOptional: decl = choice.getDecl(); break; // KeyPath application is not filtered in `performMemberLookup`. case OverloadChoiceKind::KeyPathApplication: case OverloadChoiceKind::DynamicMemberLookup: case OverloadChoiceKind::KeyPathDynamicMemberLookup: case OverloadChoiceKind::TupleIndex: case OverloadChoiceKind::MaterializePack: case OverloadChoiceKind::ExtractFunctionIsolation: return true; } // If this is a member lookup, the call arguments (if we have any) will // generally be applied to the second level of parameters, with the member // lookup applying the curried self at the first level. But there are cases // where we can get an unapplied declaration reference back. auto hasAppliedSelf = decl->hasCurriedSelf() && doesMemberRefApplyCurriedSelf(choice.getBaseType(), decl); AnyFunctionType *fnType = nullptr; if (decl->hasParameterList()) { fnType = decl->getInterfaceType()->castTo(); if (hasAppliedSelf) { fnType = fnType->getResult()->getAs(); assert(fnType && "Parameter list curry level does not match type"); } } else if (auto *VD = dyn_cast(decl)) { // For variables, we can reject any type that we know cannot be callable. auto varTy = VD->getValueInterfaceType()->lookThroughAllOptionalTypes(); if (!varTy->mayBeCallable(cs.DC)) return false; fnType = varTy->getAs(); } else if (auto *MD = dyn_cast(decl)) { fnType = MD->getInterfaceType()->getAs(); } // Given we want to be conservative with this checking, if there's any case // we can't match arguments for (e.g callable nominals, type parameters), // default to returning true. if (!fnType) return true; auto params = fnType->getParams(); ParameterListInfo paramInfo(params, decl, hasAppliedSelf); return matchCallArguments(args, params, paramInfo, unlabeledTrailingClosureArgIndex, /*allow fixes*/ false, listener, std::nullopt) .has_value(); } Expr *constraints::getArgumentLabelTargetExpr(Expr *fn) { // Dig out the function, looking through, parentheses, ?, and !. do { fn = fn->getSemanticsProvidingExpr(); if (auto force = dyn_cast(fn)) { fn = force->getSubExpr(); continue; } if (auto bind = dyn_cast(fn)) { fn = bind->getSubExpr(); continue; } return fn; } while (true); } /// Determine the default type-matching options to use when decomposing a /// constraint into smaller constraints. static ConstraintSystem::TypeMatchOptions getDefaultDecompositionOptions( ConstraintSystem::TypeMatchOptions flags) { return flags | ConstraintSystem::TMF_GenerateConstraints; } /// Whether the given parameter requires an argument. static bool parameterRequiresArgument(ArrayRef params, const ParameterListInfo ¶mInfo, unsigned paramIdx) { return !paramInfo.hasDefaultArgument(paramIdx) && !params[paramIdx].isVariadic(); } /// Determine whether the given parameter can accept a trailing closure for the /// "backward" logic. static bool backwardScanAcceptsTrailingClosure( const AnyFunctionType::Param ¶m) { Type paramTy = param.getPlainType(); if (!paramTy) return true; paramTy = paramTy->lookThroughAllOptionalTypes(); return paramTy->isTypeParameter() || paramTy->is() || paramTy->is() || paramTy->isTypeVariableOrMember() || paramTy->is() || paramTy->isAny(); } /// Determine whether any parameter from the given index up until the end /// requires an argument to be provided. /// /// \param params The parameters themselves. /// \param paramInfo Declaration-provided information about the parameters. /// \param firstParamIdx The first parameter to examine to determine whether any /// parameter in the range \c [paramIdx, params.size()) requires an argument. /// \param beforeLabel If non-empty, stop examining parameters when we reach /// a parameter with this label. static bool anyParameterRequiresArgument( ArrayRef params, const ParameterListInfo ¶mInfo, unsigned firstParamIdx, std::optional beforeLabel) { for (unsigned paramIdx : range(firstParamIdx, params.size())) { // If have been asked to stop when we reach a parameter with a particular // label, and we see a parameter with that label, we're done: no parameter // requires an argument. if (beforeLabel && *beforeLabel == params[paramIdx].getLabel()) break; // If this parameter requires an argument, tell the caller. if (parameterRequiresArgument(params, paramInfo, paramIdx)) return true; } // No parameters required arguments. return false; } static bool isCodeCompletionTypeVar(Type type) { if (auto *TVT = type->getAs()) { if (TVT->getImpl().isCodeCompletionToken()) { return true; } } return false; } static bool matchCallArgumentsImpl( SmallVectorImpl &args, ArrayRef params, const ParameterListInfo ¶mInfo, std::optional unlabeledTrailingClosureArgIndex, bool allowFixes, TrailingClosureMatching trailingClosureMatching, MatchCallArgumentListener &listener, SmallVectorImpl ¶meterBindings) { assert(params.size() == paramInfo.size() && "Default map does not match"); assert(!unlabeledTrailingClosureArgIndex || *unlabeledTrailingClosureArgIndex < args.size()); // Keep track of the parameter we're matching and what argument indices // got bound to each parameter. unsigned numParams = params.size(); parameterBindings.clear(); parameterBindings.resize(numParams); // Keep track of which arguments we have claimed from the argument tuple. unsigned numArgs = args.size(); SmallVector claimedArgs(numArgs, false); SmallVector actualArgNames; unsigned numClaimedArgs = 0; // Indicates whether any of the arguments are potentially out-of-order, // requiring further checking at the end. bool potentiallyOutOfOrder = false; // Local function that claims the argument at \c argIdx, returning the // index of the claimed argument. This is primarily a helper for // \c claimNextNamed. auto claim = [&](Identifier expectedName, unsigned argIdx, bool ignoreNameClash = false) -> unsigned { // Make sure we can claim this argument. ASSERT(argIdx != numArgs && "Must have a valid index to claim"); ASSERT(!claimedArgs[argIdx] && "Argument already claimed"); // Prevent recording of an argument label mismatche for an unlabeled // trailing closure. An unlabeled trailing closure is necessarily the first // one and vice versa, per language syntax. if (unlabeledTrailingClosureArgIndex == argIdx) { expectedName = Identifier(); } if (!actualArgNames.empty()) { // We're recording argument names; record this one. actualArgNames[argIdx] = expectedName; } else if (!ignoreNameClash && !args[argIdx].matchParameterLabel(expectedName)) { // We have an argument name mismatch. Start recording argument names. actualArgNames.resize(numArgs); // Figure out previous argument names from the parameter bindings. for (auto i : indices(params)) { const auto ¶m = params[i]; bool firstArg = true; for (auto argIdx : parameterBindings[i]) { actualArgNames[argIdx] = firstArg ? param.getLabel() : Identifier(); firstArg = false; } } // Record this argument name. actualArgNames[argIdx] = expectedName; } claimedArgs[argIdx] = true; ++numClaimedArgs; return argIdx; }; // Local function that skips over any claimed arguments. auto skipClaimedArgs = [&](unsigned &nextArgIdx) { while (nextArgIdx != numArgs && claimedArgs[nextArgIdx]) ++nextArgIdx; return nextArgIdx; }; // Local function that retrieves the next unclaimed argument with the given // name (which may be empty). This routine claims the argument. auto claimNextNamed = [&](unsigned &nextArgIdx, Identifier paramLabel, bool ignoreNameMismatch, bool forVariadic = false) -> std::optional { // Skip over any claimed arguments. skipClaimedArgs(nextArgIdx); // If we've claimed all of the arguments, there's nothing more to do. if (numClaimedArgs == numArgs) return std::nullopt; // Go hunting for an unclaimed argument whose name does match. std::optional claimedWithSameName; unsigned firstArgIdx = nextArgIdx; for (unsigned i = nextArgIdx; i != numArgs; ++i) { auto argLabel = args[i].getLabel(); bool claimIgnoringNameMismatch = false; if (!args[i].matchParameterLabel(paramLabel)) { // If this is an attempt to claim additional unlabeled arguments // for variadic parameter, we have to stop at first labeled argument. if (forVariadic) return std::nullopt; if ((i == firstArgIdx || ignoreNameMismatch) && listener.canClaimArgIgnoringNameMismatch(args[i])) { // Avoid triggering relabelling fixes about the completion arg. claimIgnoringNameMismatch = true; } else { // Otherwise we can continue trying to find argument which // matches parameter with or without label. continue; } } // Skip claimed arguments. if (claimedArgs[i]) { assert(!forVariadic && "Cannot be for a variadic claim"); // Note that we have already claimed an argument with the same name. if (!claimedWithSameName) claimedWithSameName = i; continue; } // We found a match. If the match wasn't the next one, we have // potentially out of order arguments. if (i != nextArgIdx) { assert(!forVariadic && "Cannot be for a variadic claim"); // Avoid claiming un-labeled defaulted parameters // by out-of-order un-labeled arguments or parts // of variadic argument sequence, because that might // be incorrect: // ```swift // func foo(_ a: Int, _ b: Int = 0, c: Int = 0, _ d: Int) {} // foo(1, c: 2, 3) // -> `3` will be claimed as '_ b:'. // ``` if (argLabel.empty() && !claimIgnoringNameMismatch) continue; potentiallyOutOfOrder = true; } // Claim it. return claim(paramLabel, i, claimIgnoringNameMismatch); } // If we're not supposed to attempt any fixes, we're done. if (!allowFixes) return std::nullopt; // Several things could have gone wrong here, and we'll check for each // of them at some point: // - The keyword argument might be redundant, in which case we can point // out the issue. // - The argument might be unnamed, in which case we try to fix the // problem by adding the name. // - The argument might have extraneous label, in which case we try to // fix the problem by removing such label. // - The keyword argument might be a typo for an actual argument name, in // which case we should find the closest match to correct to. // Missing or extraneous label. if (nextArgIdx != numArgs && ignoreNameMismatch) { auto argLabel = args[nextArgIdx].getLabel(); // Claim this argument if we are asked to ignore labeling failure, // only if argument doesn't have a label when parameter expected // it to, or vice versa. if (paramLabel.empty() || argLabel.empty()) return claim(paramLabel, nextArgIdx); } // Redundant keyword arguments. if (claimedWithSameName) { // FIXME: We can provide better diagnostics here. return std::nullopt; } // Typo correction is handled in a later pass. return std::nullopt; }; // Local function that attempts to bind the given parameter to arguments in // the list. bool haveUnfulfilledParams = false; auto bindNextParameter = [&](unsigned paramIdx, unsigned &nextArgIdx, bool ignoreNameMismatch) { const auto ¶m = params[paramIdx]; Identifier paramLabel = param.getLabel(); // If we have the trailing closure argument and are performing a forward // match, look for the matching parameter. if (trailingClosureMatching == TrailingClosureMatching::Forward && unlabeledTrailingClosureArgIndex && skipClaimedArgs(nextArgIdx) == *unlabeledTrailingClosureArgIndex) { // If the parameter we are looking at does not support the (unlabeled) // trailing closure argument, this parameter is unfulfilled. if (!paramInfo.acceptsUnlabeledTrailingClosureArgument(paramIdx) && !ignoreNameMismatch) { haveUnfulfilledParams = true; return; } // Let's consider current closure to be extraneous if: // // - current parameter has a default value and doesn't accept a trailing // closure; and // - no other free parameter after this one accepts a trailing closure via // forward or backward scan. This check makes sure that it's safe to // reject and push it to the next parameter without affecting backward // scan logic. // // In other words - let's push the closure argument through defaulted // parameters until it can be considered extraneous if no parameters // could possibly match it. if (!paramInfo.acceptsUnlabeledTrailingClosureArgument(paramIdx) && !parameterRequiresArgument(params, paramInfo, paramIdx)) { if (llvm::none_of( range(paramIdx + 1, params.size()), [&](unsigned idx) { return parameterBindings[idx].empty() && (paramInfo.acceptsUnlabeledTrailingClosureArgument( idx) || backwardScanAcceptsTrailingClosure(params[idx])); })) { haveUnfulfilledParams = true; return; } // If one or more parameters can match the closure, let's check // whether backward scan is applicable here. } // If this parameter does not require an argument, consider applying a // backward-match rule that skips this parameter if doing so is the only // way to successfully match arguments to parameters. if (!parameterRequiresArgument(params, paramInfo, paramIdx) && anyParameterRequiresArgument( params, paramInfo, paramIdx + 1, nextArgIdx + 1 < numArgs ? std::optional(args[nextArgIdx + 1].getLabel()) : std::optional(std::nullopt))) { haveUnfulfilledParams = true; return; } // The argument is unlabeled, so mark the parameter as unlabeled as // well. paramLabel = Identifier(); } // Handle variadic parameters. if (param.isVariadic() || isPackExpansionType(param.getPlainType())) { // Claim the next argument with the name of this parameter. auto claimed = claimNextNamed(nextArgIdx, paramLabel, ignoreNameMismatch); // If there was no such argument, leave the parameter unfulfilled. if (!claimed) { haveUnfulfilledParams = true; return; } // Record the first argument for the variadic. parameterBindings[paramIdx].push_back(*claimed); auto currentNextArgIdx = nextArgIdx; { nextArgIdx = *claimed; // Claim any additional unnamed arguments. while (true) { // If the next argument is the unlabeled trailing closure and the // variadic parameter does not accept the unlabeled trailing closure // argument, we're done. if (trailingClosureMatching == TrailingClosureMatching::Forward && unlabeledTrailingClosureArgIndex && skipClaimedArgs(nextArgIdx) == *unlabeledTrailingClosureArgIndex && !paramInfo.acceptsUnlabeledTrailingClosureArgument(paramIdx)) break; if ((claimed = claimNextNamed(nextArgIdx, Identifier(), false, true))) parameterBindings[paramIdx].push_back(*claimed); else break; } } nextArgIdx = currentNextArgIdx; return; } // Try to claim an argument for this parameter. if (auto claimed = claimNextNamed(nextArgIdx, paramLabel, ignoreNameMismatch)) { parameterBindings[paramIdx].push_back(*claimed); return; } // There was no argument to claim. Leave the argument unfulfilled. haveUnfulfilledParams = true; }; // If we have an unlabeled trailing closure and are matching backward, match // the trailing closure argument near the end. if (unlabeledTrailingClosureArgIndex && trailingClosureMatching == TrailingClosureMatching::Backward) { assert(!claimedArgs[*unlabeledTrailingClosureArgIndex]); // One past the next parameter index to look at. unsigned prevParamIdx = numParams; // Scan backwards from the end to match the unlabeled trailing closure. std::optional unlabeledParamIdx; if (prevParamIdx > 0) { unsigned paramIdx = prevParamIdx - 1; bool lastAcceptsTrailingClosure = backwardScanAcceptsTrailingClosure(params[paramIdx]); // If the last parameter is defaulted, this might be // an attempt to use a trailing closure with previous // parameter that accepts a function type e.g. // // func foo(_: () -> Int, _ x: Int = 0) {} // foo { 42 } if (!lastAcceptsTrailingClosure && paramIdx > 0 && paramInfo.hasDefaultArgument(paramIdx)) { auto paramType = params[paramIdx - 1].getPlainType(); // If the parameter before defaulted last accepts. if (paramType->is()) { lastAcceptsTrailingClosure = true; paramIdx -= 1; } } if (lastAcceptsTrailingClosure) unlabeledParamIdx = paramIdx; } // Trailing closure argument couldn't be matched to anything. Fail fast. if (!unlabeledParamIdx) { return true; } // Claim the parameter/argument pair. claim( params[*unlabeledParamIdx].getLabel(), *unlabeledTrailingClosureArgIndex, /*ignoreNameClash=*/true); parameterBindings[*unlabeledParamIdx].push_back( *unlabeledTrailingClosureArgIndex); } { unsigned nextArgIdx = 0; // Mark through the parameters, binding them to their arguments. for (auto paramIdx : indices(params)) { if (parameterBindings[paramIdx].empty()) bindNextParameter(paramIdx, nextArgIdx, false); } } // If we have any unclaimed arguments, complain about those. if (numClaimedArgs != numArgs) { // Find all of the named, unclaimed arguments. llvm::SmallVector unclaimedNamedArgs; for (auto argIdx : indices(args)) { if (claimedArgs[argIdx]) continue; if (!listener.shouldClaimArgDuringRecovery(argIdx)) continue; if (!args[argIdx].getLabel().empty()) unclaimedNamedArgs.push_back(argIdx); } if (!unclaimedNamedArgs.empty()) { // Find all of the named, unfulfilled parameters. llvm::SmallVector unfulfilledNamedParams; bool hasUnfulfilledUnnamedParams = false; for (auto paramIdx : indices(params)) { if (parameterBindings[paramIdx].empty()) { if (params[paramIdx].getLabel().empty()) hasUnfulfilledUnnamedParams = true; else unfulfilledNamedParams.push_back(paramIdx); } } if (!unfulfilledNamedParams.empty()) { // Use typo correction to find the best matches. // FIXME: There is undoubtedly a good dynamic-programming algorithm // to find the best assignment here. for (auto argIdx : unclaimedNamedArgs) { auto argName = args[argIdx].getLabel(); // Find the closest matching unfulfilled named parameter. unsigned bestScore = 0; unsigned best = 0; for (auto i : indices(unfulfilledNamedParams)) { unsigned param = unfulfilledNamedParams[i]; auto paramName = params[param].getLabel(); if (auto score = scoreParamAndArgNameTypo(paramName.str(), argName.str(), bestScore)) { if (*score < bestScore || bestScore == 0) { bestScore = *score; best = i; } } } // If we found a parameter to fulfill, do it. if (bestScore > 0) { // Bind this parameter to the argument. auto paramIdx = unfulfilledNamedParams[best]; auto paramLabel = params[paramIdx].getLabel(); parameterBindings[paramIdx].push_back(claim(paramLabel, argIdx)); // Erase this parameter from the list of unfulfilled named // parameters, so we don't try to fulfill it again. unfulfilledNamedParams.erase(unfulfilledNamedParams.begin() + best); if (unfulfilledNamedParams.empty()) break; } } // Update haveUnfulfilledParams, because we may have fulfilled some // parameters above. haveUnfulfilledParams = hasUnfulfilledUnnamedParams || !unfulfilledNamedParams.empty(); } } // Find all of the unfulfilled parameters, and match them up // semi-positionally. if (numClaimedArgs != numArgs) { // Restart at the first argument/parameter. unsigned nextArgIdx = 0; haveUnfulfilledParams = false; for (auto paramIdx : indices(params)) { // Skip fulfilled parameters. if (!parameterBindings[paramIdx].empty()) continue; bindNextParameter(paramIdx, nextArgIdx, true); if (!listener.shouldClaimArgDuringRecovery(nextArgIdx)) continue; } } // If there are as many arguments as parameters but we still // haven't claimed all of the arguments, it could mean that // labels don't line up, if so let's try to claim arguments // with incorrect labels, and let OoO/re-labeling logic diagnose that. if (numArgs == numParams && numClaimedArgs != numArgs) { for (auto i : indices(args)) { if (claimedArgs[i] || !parameterBindings[i].empty()) continue; // If parameter has a default value, we don't really // know if label doesn't match because it's incorrect // or argument belongs to some other parameter, so // we just leave this parameter unfulfilled. if (paramInfo.hasDefaultArgument(i)) continue; if (!listener.shouldClaimArgDuringRecovery(i)) continue; // Looks like there was no parameter claimed at the same // position, it could only mean that label is completely // different, because typo correction has been attempted already. parameterBindings[i].push_back(claim(params[i].getLabel(), i)); } } // If we still haven't claimed all of the arguments, // fail if there is no recovery. if (numClaimedArgs != numArgs) { for (auto index : indices(claimedArgs)) { if (claimedArgs[index]) continue; if (listener.extraArgument(index)) return true; } } // FIXME: If we had the actual parameters and knew the body names, those // matches would be best. potentiallyOutOfOrder = true; } // If we have any unfulfilled parameters, check them now. std::optional prevArgIdx; if (haveUnfulfilledParams) { for (auto paramIdx : indices(params)) { // If we have a binding for this parameter, we're done. if (!parameterBindings[paramIdx].empty()) { prevArgIdx = parameterBindings[paramIdx].back(); continue; } const auto ¶m = params[paramIdx]; // Variadic parameters can be unfulfilled. if (param.isVariadic() || isPackExpansionType(param.getPlainType())) continue; // Parameters with defaults can be unfulfilled. if (paramInfo.hasDefaultArgument(paramIdx)) continue; unsigned argInsertIdx = prevArgIdx ? *prevArgIdx + 1 : 0; if (auto newArgIdx = listener.missingArgument(paramIdx, argInsertIdx)) { parameterBindings[paramIdx].push_back(*newArgIdx); continue; } return true; } } // If any arguments were provided out-of-order, check whether we have // violated any of the reordering rules. if (potentiallyOutOfOrder) { // If we've seen label failures and now there is an out-of-order // parameter (or even worse - OoO parameter with label re-naming), // we most likely have no idea what would be the best // diagnostic for this situation, so let's just try to re-label. auto isOutOfOrderArgument = [&](unsigned toParamIdx, unsigned fromArgIdx, unsigned toArgIdx) { if (fromArgIdx <= toArgIdx) { return false; } auto newLabel = args[fromArgIdx].getLabel(); auto oldLabel = args[toArgIdx].getLabel(); if (newLabel != params[toParamIdx].getLabel()) { return false; } auto paramIdx = toParamIdx + 1; for (; paramIdx < params.size(); ++paramIdx) { // Looks like new position (excluding defaulted parameters), // has a valid label. if (oldLabel == params[paramIdx].getLabel()) break; // If we are moving the position with a different label // and there is no default value for it, can't diagnose the // problem as a simple re-ordering. if (!paramInfo.hasDefaultArgument(paramIdx)) return false; } // label was not found if (paramIdx == params.size()) { return false; } return true; }; SmallVector paramToArgMap; paramToArgMap.reserve(params.size()); { unsigned argIdx = 0; for (const auto &binding : parameterBindings) { paramToArgMap.push_back(argIdx); // Ignore argument bindings that were synthesized due to missing args. argIdx += llvm::count_if( binding, [numArgs](unsigned argIdx) { return argIdx < numArgs; }); } } // Enumerate the parameters and their bindings to see if any arguments are // our of order bool hadLabelMismatch = false; for (const auto paramIdx : indices(params)) { const auto toArgIdx = paramToArgMap[paramIdx]; const auto &binding = parameterBindings[paramIdx]; for (const auto paramBindIdx : indices(binding)) { // We've found the parameter that has an out of order // argument, and know the indices of the argument that // needs to move (fromArgIdx) and the argument location // it should move to (toArgIdx). const auto fromArgIdx = binding[paramBindIdx]; // Ignore argument bindings that were synthesized due to missing args. if (fromArgIdx >= numArgs) continue; // Does nothing for variadic tail. if ((params[paramIdx].isVariadic() || isPackExpansionType(params[paramIdx].getPlainType())) && paramBindIdx > 0) { assert(args[fromArgIdx].getLabel().empty()); continue; } // First let's double check if out-of-order argument is nothing // more than a simple label mismatch, because in situation where // one argument requires label and another one doesn't, but caller // doesn't provide either, problem is going to be identified as // out-of-order argument instead of label mismatch. const auto expectedLabel = fromArgIdx == unlabeledTrailingClosureArgIndex ? Identifier() : params[paramIdx].getLabel(); const auto argumentLabel = args[fromArgIdx].getLabel(); if (argumentLabel != expectedLabel) { // - The parameter is unnamed, in which case we try to fix the // problem by removing the name. if (expectedLabel.empty()) { hadLabelMismatch = true; if (listener.extraneousLabel(paramIdx)) return true; // - The argument is unnamed, in which case we try to fix the // problem by adding the name. } else if (argumentLabel.empty()) { hadLabelMismatch = true; if (listener.missingLabel(paramIdx)) return true; // - The argument label has a typo at the same position. } else if (fromArgIdx == toArgIdx) { hadLabelMismatch = true; if (listener.incorrectLabel(paramIdx)) return true; } } if (fromArgIdx == toArgIdx) { // If the argument is in the right location, just continue continue; } // This situation looks like out-of-order argument but it's hard // to say exactly without considering other factors, because it // could be invalid labeling too. if (!hadLabelMismatch && isOutOfOrderArgument(paramIdx, fromArgIdx, toArgIdx)) { return listener.outOfOrderArgument( fromArgIdx, toArgIdx, parameterBindings); } SmallVector expectedLabels; llvm::transform(params, std::back_inserter(expectedLabels), [](const AnyFunctionType::Param ¶m) { return param.getLabel(); }); return listener.relabelArguments(expectedLabels); } } } // If no arguments were renamed, the call arguments match up with the // parameters. if (actualArgNames.empty()) return false; // The arguments were relabeled; notify the listener. return listener.relabelArguments(actualArgNames); } /// Determine whether call-argument matching requires us to try both the /// forward and backward scanning directions to succeed. static bool requiresBothTrailingClosureDirections( ArrayRef args, ArrayRef params, const ParameterListInfo ¶mInfo, std::optional unlabeledTrailingClosureArgIndex) { // If there's no unlabeled trailing closure, direction doesn't matter. if (!unlabeledTrailingClosureArgIndex) return false; // If there are labeled trailing closure arguments, only scan forward. if (*unlabeledTrailingClosureArgIndex < args.size() - 1) return false; // If there are no parameters, it doesn't matter; only scan forward. if (params.empty()) return false; // If backward matching is disabled, only scan forward. ASTContext &ctx = params.front().getPlainType()->getASTContext(); if (ctx.LangOpts.hasFeature(Feature::ForwardTrailingClosures)) return false; // If there are at least two parameters that meet the backward scan's // definition of "accepts trailing closure", or there is one such parameter // with a defaulted parameter after it, we'll need to do the scan // in both directions. bool sawAnyTrailingClosureParam = false; for (unsigned paramIdx : indices(params)) { const auto ¶m = params[paramIdx]; if (backwardScanAcceptsTrailingClosure(param)) { if (sawAnyTrailingClosureParam) return true; sawAnyTrailingClosureParam = true; continue; } if (sawAnyTrailingClosureParam && paramInfo.hasDefaultArgument(paramIdx)) return true; } // Only one parameter can match the trailing closure anyway, so don't bother // scanning twice. return false; } std::optional constraints::matchCallArguments( SmallVectorImpl &args, ArrayRef params, const ParameterListInfo ¶mInfo, std::optional unlabeledTrailingClosureArgIndex, bool allowFixes, MatchCallArgumentListener &listener, std::optional trailingClosureMatching) { /// Perform a single call to the implementation of matchCallArguments, /// invoking the listener and using the results from that match. auto singleMatchCall = [&](TrailingClosureMatching scanDirection) -> std::optional { SmallVector paramBindings; if (matchCallArgumentsImpl( args, params, paramInfo, unlabeledTrailingClosureArgIndex, allowFixes, scanDirection, listener, paramBindings)) return std::nullopt; return MatchCallArgumentResult{scanDirection, std::move(paramBindings), std::nullopt}; }; // If we know that we won't have to perform both forward and backward // scanning for trailing closures, fast-path by performing just the // appropriate scan. if (trailingClosureMatching || !requiresBothTrailingClosureDirections( args, params, paramInfo, unlabeledTrailingClosureArgIndex)) { return singleMatchCall( trailingClosureMatching.value_or(TrailingClosureMatching::Forward)); } MatchCallArgumentListener noOpListener; // Try the forward direction first. SmallVector forwardParamBindings; bool forwardFailed = matchCallArgumentsImpl( args, params, paramInfo, unlabeledTrailingClosureArgIndex, allowFixes, TrailingClosureMatching::Forward, noOpListener, forwardParamBindings); // Try the backward direction. SmallVector backwardParamBindings; bool backwardFailed = matchCallArgumentsImpl( args, params, paramInfo, unlabeledTrailingClosureArgIndex, allowFixes, TrailingClosureMatching::Backward, noOpListener, backwardParamBindings); // If at least one of them failed, or they produced the same results, run // call argument matching again with the real visitor. if (forwardFailed || backwardFailed || forwardParamBindings == backwardParamBindings) { // Run the forward scan unless the backward scan is the only one that // succeeded. auto scanDirection = backwardFailed || !forwardFailed ? TrailingClosureMatching::Forward : TrailingClosureMatching::Backward; return singleMatchCall(scanDirection); } // Both forward and backward succeeded, and produced different results. // Bundle them up and return both---without invoking the listener---so the // solver can choose. return MatchCallArgumentResult{ TrailingClosureMatching::Forward, std::move(forwardParamBindings), std::move(backwardParamBindings) }; } bool CompletionArgInfo::allowsMissingArgAt(unsigned argInsertIdx, AnyFunctionType::Param param) { // If the argument is before or at the index of the argument containing the // completion, the user would likely have already written it if they // intended this overload. if (completionIdx >= argInsertIdx) { return false; } // If the argument is after the first trailing closure, the user can only // continue on to write more trailing arguments, so only allow this overload // if the missing argument is of function type. if (firstTrailingIdx && argInsertIdx > *firstTrailingIdx) { if (param.isInOut()) { return false; } Type expectedTy = param.getPlainType()->lookThroughAllOptionalTypes(); return expectedTy->is() || expectedTy->isAny() || expectedTy->isTypeVariableOrMember(); } return true; } std::optional constraints::getCompletionArgInfo(ASTNode anchor, ConstraintSystem &CS) { auto *exprAnchor = getAsExpr(anchor); if (!exprAnchor) return std::nullopt; auto *args = exprAnchor->getArgs(); if (!args) return std::nullopt; for (unsigned i : indices(*args)) { if (CS.containsIDEInspectionTarget(args->getExpr(i))) return CompletionArgInfo{i, args->getFirstTrailingClosureIndex(), args->size()}; } return std::nullopt; } class ArgumentFailureTracker : public MatchCallArgumentListener { protected: ConstraintSystem &CS; NullablePtr Callee; SmallVectorImpl &Arguments; ArrayRef Parameters; std::optional UnlabeledTrailingClosureArgIndex; ConstraintLocatorBuilder Locator; private: SmallVector MissingArguments; SmallVector, 4> ExtraArguments; protected: /// Synthesizes an argument that is intended to match against a missing /// argument for the parameter at \p paramIdx. /// \returns The index of the new argument in \c Arguments. unsigned synthesizeArgument(unsigned paramIdx, bool isAfterCodeCompletionLoc) { const auto ¶m = Parameters[paramIdx]; unsigned newArgIdx = Arguments.size(); auto *argLoc = CS.getConstraintLocator( Locator, {LocatorPathElt::ApplyArgToParam(newArgIdx, paramIdx, param.getParameterFlags()), LocatorPathElt::SynthesizedArgument( newArgIdx, isAfterCodeCompletionLoc)}); auto *argType = CS.createTypeVariable( argLoc, TVO_CanBindToInOut | TVO_CanBindToLValue | TVO_CanBindToNoEscape | TVO_CanBindToHole); auto synthesizedArg = param.withType(argType); Arguments.push_back(synthesizedArg); return newArgIdx; } public: ArgumentFailureTracker( ConstraintSystem &cs, ValueDecl *callee, SmallVectorImpl &args, ArrayRef params, std::optional unlabeledTrailingClosureArgIndex, ConstraintLocatorBuilder locator) : CS(cs), Callee(callee), Arguments(args), Parameters(params), UnlabeledTrailingClosureArgIndex(unlabeledTrailingClosureArgIndex), Locator(locator) {} ~ArgumentFailureTracker() override { if (!MissingArguments.empty()) { auto *fix = AddMissingArguments::create(CS, MissingArguments, CS.getConstraintLocator(Locator)); // Not having an argument is the same impact as having a type mismatch. (void)CS.recordFix(fix, /*impact=*/MissingArguments.size() * 2); } } std::optional missingArgument(unsigned paramIdx, unsigned argInsertIdx) override { if (!CS.shouldAttemptFixes()) return std::nullopt; unsigned newArgIdx = synthesizeArgument(paramIdx, /*isAfterCodeCompletionLoc=*/false); auto synthesizedArg = Arguments[newArgIdx]; MissingArguments.push_back(SynthesizedArg{paramIdx, synthesizedArg}); return newArgIdx; } bool extraArgument(unsigned argIdx) override { if (!CS.shouldAttemptFixes()) return true; // If this is a trailing closure, let's check if the call is // to an init of a callable type. If so, let's not record it // as extraneous since it would be matched against implicitly // injected `.callAsFunction` call. if (UnlabeledTrailingClosureArgIndex && argIdx == *UnlabeledTrailingClosureArgIndex && Callee) { if (auto *ctor = dyn_cast(Callee.get())) { auto resultTy = ctor->getResultInterfaceType(); if (resultTy->isCallAsFunctionType(CS.DC)) return true; } } ExtraArguments.push_back(std::make_pair(argIdx, Arguments[argIdx])); return false; } bool missingLabel(unsigned paramIndex) override { return !CS.shouldAttemptFixes(); } bool extraneousLabel(unsigned paramIndex) override { return !CS.shouldAttemptFixes(); } bool incorrectLabel(unsigned paramIndex) override { return !CS.shouldAttemptFixes(); } bool outOfOrderArgument( unsigned argIdx, unsigned prevArgIdx, ArrayRef bindings) override { if (CS.shouldAttemptFixes()) { // If some of the arguments are missing/extraneous, no reason to // record a fix for this, increase the score so there is a way // to identify that there is something going on besides just missing // arguments. if (!MissingArguments.empty() || !ExtraArguments.empty()) { CS.increaseScore(SK_Fix, Locator); return false; } auto *fix = MoveOutOfOrderArgument::create( CS, argIdx, prevArgIdx, bindings, CS.getConstraintLocator(Locator)); return CS.recordFix(fix); } return true; } bool relabelArguments(ArrayRef newLabels) override { if (!CS.shouldAttemptFixes()) return true; // TODO(diagnostics): If re-labeling is mixed with extra arguments, // let's produce a fix only for extraneous arguments for now, // because they'd share a locator path which (currently) means // one fix would overwrite another. if (!ExtraArguments.empty()) { CS.increaseScore(SK_Fix, Locator); return false; } auto anchor = Locator.getBaseLocator()->getAnchor(); if (!anchor) return true; unsigned numExtraneous = 0; unsigned numRenames = 0; unsigned numOutOfOrder = 0; for (unsigned i : indices(newLabels)) { // It's already known how many arguments are missing, // it would be accounted for in the impact. if (i >= Arguments.size()) continue; auto argLabel = Arguments[i].getLabel(); auto paramLabel = newLabels[i]; if (argLabel == paramLabel) continue; if (!argLabel.empty()) { // Instead of this being a label mismatch which requires // re-labeling, this could be an out-of-order argument // instead which has a completely different impact. if (llvm::count(newLabels, argLabel) == 1) { ++numOutOfOrder; } else if (paramLabel.empty()) { ++numExtraneous; } else { ++numRenames; } } } auto *locator = CS.getConstraintLocator(Locator); auto *fix = RelabelArguments::create(CS, newLabels, locator); // Re-labeling fixes with extraneous/incorrect labels should be // lower priority vs. other fixes on same/different overload(s) // where labels did line up correctly. // // If there are not only labeling problems but also some of the // arguments are missing, let's account of that in the impact. auto impact = 1 + numOutOfOrder + numExtraneous * 2 + numRenames * 3 + MissingArguments.size() * 2; return CS.recordFix(fix, impact); } ArrayRef> getExtraneousArguments() const { return ExtraArguments; } }; /// Ignores any failures after the code completion token. class CompletionArgumentTracker : public ArgumentFailureTracker { struct CompletionArgInfo ArgInfo; public: CompletionArgumentTracker( ConstraintSystem &cs, ValueDecl *callee, SmallVectorImpl &args, ArrayRef params, std::optional unlabeledTrailingClosureArgIndex, ConstraintLocatorBuilder locator, struct CompletionArgInfo ArgInfo) : ArgumentFailureTracker(cs, callee, args, params, unlabeledTrailingClosureArgIndex, locator), ArgInfo(ArgInfo) {} std::optional missingArgument(unsigned paramIdx, unsigned argInsertIdx) override { // When solving for code completion, if any argument contains the // completion location, later arguments shouldn't be considered missing // (causing the solution to have a worse score) as the user just hasn't // written them yet. Early exit to avoid recording them in this case. if (ArgInfo.allowsMissingArgAt(argInsertIdx, Parameters[paramIdx])) { return synthesizeArgument(paramIdx, /*isAfterCodeCompletionLoc=*/true); } return ArgumentFailureTracker::missingArgument(paramIdx, argInsertIdx); } bool extraArgument(unsigned argIdx) override { if (ArgInfo.isBefore(argIdx)) { return false; } if (argIdx == 0 && ArgInfo.completionIdx == 0) { return false; } return ArgumentFailureTracker::extraArgument(argIdx); } bool outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx, ArrayRef bindings) override { if (ArgInfo.isBefore(argIdx)) { return false; } return ArgumentFailureTracker::outOfOrderArgument(argIdx, prevArgIdx, bindings); } bool shouldClaimArgDuringRecovery(unsigned argIdx) override { return !ArgInfo.isBefore(argIdx); } bool canClaimArgIgnoringNameMismatch(const AnyFunctionType::Param &arg) override { if (!isCodeCompletionTypeVar(arg.getPlainType())) { return false; } if (!arg.getLabel().empty()) { return false; } return true; } }; class AllowLabelMismatches : public MatchCallArgumentListener { SmallVector NewLabels; bool HadLabelingIssues = false; public: bool missingLabel(unsigned paramIndex) override { HadLabelingIssues = true; return false; } bool relabelArguments(ArrayRef newLabels) override { NewLabels.append(newLabels.begin(), newLabels.end()); HadLabelingIssues = true; return false; } bool hadLabelingIssues() const { return HadLabelingIssues; } std::optional> getLabelReplacements() const { if (!hadLabelingIssues() || NewLabels.empty()) return std::nullopt; return {NewLabels}; } }; static std::optional> shouldOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx, Type paramTy, Type argTy, Expr *argExpr, ConstraintSystem &cs) { auto result = canOpenExistentialCallArgument(callee, paramIdx, paramTy, argTy); if (!result) return std::nullopt; // An argument expression that explicitly coerces to an existential // disables the implicit opening of the existential unless it's // wrapped in parens. if (argExpr) { if (auto argCast = dyn_cast( argExpr->getSemanticsProvidingExpr())) { if (auto typeRepr = argCast->getCastTypeRepr()) { if (auto toType = cs.getType(typeRepr)) { if (!isa(argExpr) && toType->isAnyExistentialType()) return std::nullopt; } } } } return result; } // Match the argument of a call to the parameter. static ConstraintSystem::TypeMatchResult matchCallArguments( ConstraintSystem &cs, FunctionType *contextualType, ArgumentList *argList, ArrayRef args, ArrayRef params, ConstraintKind subKind, ConstraintLocatorBuilder locator, std::optional trailingClosureMatching, SmallVectorImpl> &openedExistentials) { assert(subKind == ConstraintKind::OperatorArgumentConversion || subKind == ConstraintKind::ArgumentConversion); auto *loc = cs.getConstraintLocator(locator); assert(loc->isLastElement()); ValueDecl *callee = nullptr; bool appliedSelf = false; // Resolve the callee for the application. auto *calleeLocator = cs.getCalleeLocator(loc); if (auto overload = cs.findSelectedOverloadFor(calleeLocator)) { callee = overload->choice.getDeclOrNull(); appliedSelf = hasAppliedSelf(cs, overload->choice); } ParameterListInfo paramInfo(params, callee, appliedSelf); // Make sure that argument list is available. assert(argList); // Apply labels to arguments. SmallVector argsWithLabels; argsWithLabels.append(args.begin(), args.end()); AnyFunctionType::relabelParams(argsWithLabels, argList); // Special case when a single tuple argument if used // instead of N distinct arguments e.g.: // // func foo(_ x: Int, _ y: Int) {} // foo((1, 2)) // expected 2 arguments, got a single tuple with 2 elements. if (cs.shouldAttemptFixes() && argsWithLabels.size() == 1 && llvm::count_if(indices(params), [&](unsigned paramIdx) { return !paramInfo.hasDefaultArgument(paramIdx); }) > 1) { const auto &arg = argsWithLabels.front(); auto argTuple = arg.getPlainType()->getRValueType()->getAs(); // Don't explode a tuple in cases where first parameter is a tuple as // well. That is a regular "missing argument case" even if their arity // is different e.g. // // func foo(_: (Int, Int), _: Int) {} // foo((1, 2)) // call is missing an argument for parameter #1 if (argTuple && argTuple->getNumElements() == params.size() && !params.front().getPlainType()->is()) { argsWithLabels.pop_back(); // Let's make sure that labels associated with tuple elements // line up with what is expected by argument list. SmallVector synthesizedArgs; for (unsigned i = 0, n = argTuple->getNumElements(); i != n; ++i) { const auto &elt = argTuple->getElement(i); // If tuple doesn't have a label for its first element // and parameter does, let's assume parameter's label // to aid argument matching. For example: // // \code // func test(val: Int, _: String) {} // // test(val: (42, "")) // expands into `(val: 42, "")` // \endcode Identifier label = elt.getName(); if (i == 0 && !elt.hasName() && params[0].hasLabel()) { label = params[0].getLabel(); } AnyFunctionType::Param argument(elt.getType(), label); synthesizedArgs.push_back(SynthesizedArg{i, argument}); argsWithLabels.push_back(argument); } (void)cs.recordFix( AddMissingArguments::create(cs, synthesizedArgs, cs.getConstraintLocator(locator)), /*impact=*/synthesizedArgs.size() * 2); } } // Match up the call arguments to the parameters. SmallVector parameterBindings; TrailingClosureMatching selectedTrailingMatching = TrailingClosureMatching::Forward; { std::unique_ptr listener; if (cs.isForCodeCompletion()) { if (auto completionInfo = getCompletionArgInfo(locator.getAnchor(), cs)) { listener = std::make_unique( cs, callee, argsWithLabels, params, argList->getFirstTrailingClosureIndex(), locator, *completionInfo); } } if (!listener) { // We didn't create an argument tracker for code completion. Create a // normal one. listener = std::make_unique( cs, callee, argsWithLabels, params, argList->getFirstTrailingClosureIndex(), locator); } auto callArgumentMatch = constraints::matchCallArguments( argsWithLabels, params, paramInfo, argList->getFirstTrailingClosureIndex(), cs.shouldAttemptFixes(), *listener, trailingClosureMatching); if (!callArgumentMatch) return cs.getTypeMatchFailure(locator); // If there are different results for both the forward and backward // scans, return an ambiguity: the caller will need to build a // disjunction. if (callArgumentMatch->backwardParameterBindings) { return cs.getTypeMatchAmbiguous(); } selectedTrailingMatching = callArgumentMatch->trailingClosureMatching; // Record the matching direction and parameter bindings used for this call. cs.recordMatchCallArgumentResult(cs.getConstraintLocator(locator), *callArgumentMatch); // If there was a disjunction because both forward and backward were // possible, increase the score for forward matches to bias toward the // (source-compatible) backward matches. The compiler will produce a // warning for such code. if (trailingClosureMatching && *trailingClosureMatching == TrailingClosureMatching::Forward) cs.increaseScore(SK_ForwardTrailingClosure, locator); // Take the parameter bindings we selected. parameterBindings = std::move(callArgumentMatch->parameterBindings); auto extraArguments = listener->getExtraneousArguments(); if (!extraArguments.empty()) { if (RemoveExtraneousArguments::isMinMaxNameShadowing(cs, locator)) return cs.getTypeMatchFailure(locator); // First let's see whether this is a situation where a single // parameter is a tuple, but N distinct arguments were passed in. if (AllowTupleSplatForSingleParameter::attempt( cs, argsWithLabels, params, parameterBindings, locator)) { // Let's produce a generic "extraneous arguments" // diagnostic otherwise. auto *fix = RemoveExtraneousArguments::create( cs, contextualType, extraArguments, cs.getConstraintLocator(locator)); for (const auto &extraArg : extraArguments) { auto argument = argList->get(extraArg.first); auto argType = extraArg.second.getPlainType(); // Prevent closure resolution by binding it to a placeholder // because the main issue here is invalid overload and // errors produced from the closure body are going to be // superfluous. if (isExpr(argument.getExpr())) { cs.recordTypeVariablesAsHoles(argType); } else { cs.recordAnyTypeVarAsPotentialHole(argType); } } if (cs.recordFix(fix, /*impact=*/extraArguments.size() * 5)) return cs.getTypeMatchFailure(locator); } } } auto isSynthesizedArgument = [](const AnyFunctionType::Param &arg) -> bool { if (auto *typeVar = arg.getPlainType()->getAs()) { auto *locator = typeVar->getImpl().getLocator(); return locator->isLastElement(); } return false; }; for (unsigned paramIdx = 0, numParams = parameterBindings.size(); paramIdx != numParams; ++paramIdx){ // Determine the parameter type. const auto ¶m = params[paramIdx]; auto paramTy = param.getOldType(); // Type parameter packs ingest the entire set of argument bindings // as a pack type. // // We pull these out special because variadic parameters ban lots of // the more interesting typing constructs called out below like // inout and @autoclosure. if (paramInfo.isVariadicGenericParameter(paramIdx)) { // If generic parameter comes from a variadic type declaration it's // possible that it got specialized early and is no longer represented // by a pack expansion type. For example, consider expression - // `Test(42)` where `Test` and the initializer // is declared as `init(_: repeat each T)`. Although declaration // based information reports parameter at index 0 as variadic generic // the call site specializes it to `Int`. if (isPackExpansionType(paramTy)) { SmallVector argTypes; for (auto argIdx : parameterBindings[paramIdx]) { auto argType = argsWithLabels[argIdx].getPlainType(); argTypes.push_back(argType); } auto *argPack = PackType::get(cs.getASTContext(), argTypes); auto argPackExpansion = [&]() { if (argPack->getNumElements() == 1 && argPack->getElementType(0)->is()) { return argPack->getElementType(0)->castTo(); } return PackExpansionType::get(argPack, argPack); }(); auto firstArgIdx = argTypes.empty() ? paramIdx : parameterBindings[paramIdx].front(); cs.addConstraint( subKind, argPackExpansion, paramTy, locator.withPathElement(LocatorPathElt::ApplyArgToParam( firstArgIdx, paramIdx, param.getParameterFlags()))); continue; } } // If type inference from default arguments is enabled, let's // add a constraint from the parameter if necessary, otherwise // there is nothing to do but move to the next parameter. if (parameterBindings[paramIdx].empty() && callee) { // Type inference from default value expressions. { auto *paramList = callee->getParameterList(); if (!paramList) continue; // There is nothing to infer if parameter doesn't have any // generic parameters in its type. auto *PD = paramList->get(paramIdx); if (!PD->getInterfaceType()->hasTypeParameter()) continue; // The type of the default value is going to be determined // based on a type deduced for the parameter at this call site. if (PD->hasCallerSideDefaultExpr()) continue; auto defaultExprType = PD->getTypeOfDefaultExpr(); // A caller side default. if (!defaultExprType || defaultExprType->hasError()) continue; // If this is just a regular default type that should // work for all substitutions of generic parameter, // let's continue. if (defaultExprType->hasArchetype()) continue; cs.addConstraint( ConstraintKind::ArgumentConversion, paramTy, defaultExprType, locator.withPathElement(LocatorPathElt::ApplyArgToParam( paramIdx, paramIdx, param.getParameterFlags()))); } continue; } // See if we have a parameter label specified in the function's DeclNameLoc. Identifier compoundParamLabel; if (auto *E = getAsExpr(calleeLocator->getAnchor())) { auto nameLoc = E->getNameLoc(); if (auto labelLoc = nameLoc.getArgumentLabelLoc(paramIdx)) { auto &ctx = cs.getASTContext(); auto labelTok = Lexer::getTokenAtLocation(ctx.SourceMgr, labelLoc); compoundParamLabel = ctx.getIdentifier(labelTok.getText()); } } // Compare each of the bound arguments for this parameter. for (auto argIdx : parameterBindings[paramIdx]) { auto loc = locator.withPathElement(LocatorPathElt::ApplyArgToParam( argIdx, paramIdx, param.getParameterFlags())); const auto &argument = argsWithLabels[argIdx]; auto argTy = argument.getOldType(); bool matchingAutoClosureResult = param.isAutoClosure(); auto *argExpr = getArgumentExpr(locator.getAnchor(), argIdx); if (param.isAutoClosure() && !isSynthesizedArgument(argument)) { auto &ctx = cs.getASTContext(); // If this is a call to a function with a closure argument and the // parameter is an autoclosure, let's just increment the score here // so situations like below are not ambiguous. // func f(_: () -> T) {} // func f(_: @autoclosure () -> T) {} // // f { } // OK if (isExpr(argExpr)) { cs.increaseScore(SK_FunctionToAutoClosureConversion, loc); } // If the argument is not marked as @autoclosure or // this is Swift version >= 5 where forwarding is not allowed, // argument would always be wrapped into an implicit closure // at the end, so we can safely match against result type. if (ctx.isSwiftVersionAtLeast(5) || !isAutoClosureArgument(argExpr)) { // In Swift >= 5 mode there is no @autoclosure forwarding, // so let's match result types. if (auto *fnType = paramTy->getAs()) { paramTy = fnType->getResult(); } } else { // Matching @autoclosure argument to @autoclosure parameter // directly would mean introducing a function conversion // in Swift <= 4 mode. cs.increaseScore(SK_FunctionConversion, loc); matchingAutoClosureResult = false; } } // In case solver matched trailing based on the backward scan, // let's produce a warning which would suggest to add a label // to disambiguate in the future. if (selectedTrailingMatching == TrailingClosureMatching::Backward && argIdx == *argList->getFirstTrailingClosureIndex()) { cs.recordFix(SpecifyLabelToAssociateTrailingClosure::create( cs, cs.getConstraintLocator(loc))); } // Type-erase any opened existentials from subsequent parameter types // unless the argument itself is a generic function, which could handle // the opened existentials. if (!openedExistentials.empty() && paramTy->hasTypeVariable() && !cs.isArgumentGenericFunction(argTy, argExpr)) { for (const auto &opened : openedExistentials) { paramTy = typeEraseOpenedExistentialReference( paramTy, opened.second->getExistentialType(), opened.first, TypePosition::Contravariant); } } // If the argument is an existential type and the parameter is generic, // consider opening the existential type. if (auto typeVarAndBindingTy = shouldOpenExistentialCallArgument( callee, paramIdx, paramTy, argTy, argExpr, cs)) { // My kingdom for a decent "if let" in C++. TypeVariableType *typeVar; Type bindingTy; std::tie(typeVar, bindingTy) = *typeVarAndBindingTy; ExistentialArchetypeType *openedArchetype; // Open the argument type. argTy = argTy.transformRec([&](TypeBase *t) -> std::optional { if (t->isAnyExistentialType()) { Type openedTy; std::tie(openedTy, openedArchetype) = cs.openAnyExistentialType(t, cs.getConstraintLocator(loc)); return openedTy; } return std::nullopt; }); openedExistentials.push_back({typeVar, openedArchetype}); } // If we have a compound function reference (e.g `fn($x:)`), respect // the parameter label given. Otherwise look at the argument label. auto wrapperArgLabel = compoundParamLabel.empty() ? argument.getLabel() : compoundParamLabel; if (paramInfo.hasExternalPropertyWrapper(paramIdx) || wrapperArgLabel.hasDollarPrefix()) { auto *param = getParameterAt(callee, paramIdx); assert(param); if (cs.applyPropertyWrapperToParameter(paramTy, argTy, const_cast(param), wrapperArgLabel, subKind, cs.getConstraintLocator(loc), calleeLocator) .isFailure()) { return cs.getTypeMatchFailure(loc); } continue; } // If argument comes for declaration it should loose // `@autoclosure` flag, because in context it's used // as a function type represented by autoclosure. // // Special case here are synthesized arguments because // they mirror parameter flags to ease diagnosis. assert(!argsWithLabels[argIdx].isAutoClosure() || isSynthesizedArgument(argument)); // If parameter is a generic parameter, let's copy its // conformance requirements (if any), to the argument // be able to filter mismatching choices earlier. if (auto *typeVar = paramTy->getAs()) { auto *locator = typeVar->getImpl().getLocator(); if (locator->isForGenericParameter()) { auto &CG = cs.getConstraintGraph(); auto isTransferableConformance = [&typeVar](Constraint *constraint) { if (constraint->getKind() != ConstraintKind::ConformsTo && constraint->getKind() != ConstraintKind::NonisolatedConformsTo) return false; auto requirementTy = constraint->getFirstType(); if (!requirementTy->isEqual(typeVar)) return false; return constraint->getSecondType()->is(); }; for (auto *constraint : CG[typeVar].getConstraints()) { if (isTransferableConformance(constraint)) cs.addConstraint(ConstraintKind::TransitivelyConformsTo, argTy, constraint->getSecondType(), constraint->getLocator()); } } } // Detect that there is sync -> async mismatch early on for // closure argument to avoid re-checking calls if there was // an overload choice with synchronous parameter of the same // shape e.g. // // func test(_: () -> Void) -> MyStruct {} // func test(_: () async -> Void) -> MyStruct {} // // test({ ... }).... // // Synchronous overload is always better in this case so there // is no need to re-check follow-up ``s and better // to short-circuit this path early. if (auto *fnType = paramTy->getAs()) { if (fnType->isAsync()) { auto *typeVar = argTy->getAs(); if (typeVar && typeVar->getImpl().isClosureType()) { auto *locator = typeVar->getImpl().getLocator(); auto *closure = castToExpr(locator->getAnchor()); if (!cs.getClosureType(closure)->isAsync()) cs.increaseScore(SK_SyncInAsync, locator); } } } if (!argument.isCompileTimeLiteral() && param.isCompileTimeLiteral()) { auto *locator = cs.getConstraintLocator(loc); SourceRange range; // simplify locator so the anchor is the exact argument. cs.recordFix(NotCompileTimeLiteral::create(cs, paramTy, simplifyLocator(cs, locator, range))); } cs.addConstraint( subKind, argTy, paramTy, matchingAutoClosureResult ? loc.withPathElement(ConstraintLocator::AutoclosureResult) : loc, /*isFavored=*/false); } } return cs.getTypeMatchSuccess(); } ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionResultTypes(Type expectedResult, Type fnResult, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { // If we have a callee with an IUO return, add a disjunction that can either // bind to the result or an unwrapped result. auto *calleeLoc = getCalleeLocator(getConstraintLocator(locator)); auto *calleeResultLoc = getConstraintLocator( calleeLoc, ConstraintLocator::FunctionResult); auto selected = findSelectedOverloadFor(calleeLoc); // If we don't have a direct callee, this might be the second application // of a curried function reference, in which case we need to dig into the // inner call to find the callee. // FIXME: This is a bit of a hack. We should consider rewriting curried // applies as regular applies in PreCheckExpr to eliminate the need to special // case double applies in the solver. bool isSecondApply = false; if (!selected) { auto anchor = locator.getAnchor(); if (auto *callExpr = getAsExpr(anchor)) { if (auto *innerCall = getAsExpr(callExpr->getSemanticFn())) { auto *innerCalleeLoc = getCalleeLocator(getConstraintLocator(innerCall)); if (auto innerOverload = findSelectedOverloadFor(innerCalleeLoc)) { auto choice = innerOverload->choice; if (choice.getFunctionRefInfo().isDoubleApply()) { isSecondApply = true; selected.emplace(*innerOverload); } } } } } if (selected) { auto choice = selected->choice; // Subscripts found through dynamic lookup need special treatment. Unlike // other decls found through dynamic lookup, they cannot have an optional // applied to their reference, instead it's applied to their result. As // such, we may need to unwrap another level of optionality. if (choice.getKind() == OverloadChoiceKind::DeclViaDynamic && isa(choice.getDecl())) { // Introduce a type variable to record whether we needed to unwrap the // outer optional. auto outerTy = createTypeVariable(calleeResultLoc, TVO_CanBindToLValue); buildDisjunctionForDynamicLookupResult(outerTy, fnResult, calleeResultLoc); fnResult = outerTy; } auto iuoKind = choice.getIUOReferenceKind(*this, isSecondApply); if (iuoKind == IUOReferenceKind::ReturnValue) { buildDisjunctionForImplicitlyUnwrappedOptional(expectedResult, fnResult, calleeResultLoc); return getTypeMatchSuccess(); } } return matchTypes(expectedResult, fnResult, ConstraintKind::Bind, flags, locator); } static bool isInPatternMatchingContext(ConstraintLocatorBuilder locator) { SmallVector path; (void)locator.getLocatorParts(path); auto pathElement = llvm::find_if(path, [](LocatorPathElt &elt) { return elt.is(); }); return pathElement != path.end(); } namespace { class TupleMatcher { TupleType *tuple1; TupleType *tuple2; public: enum class MatchKind : uint8_t { Equality, Subtype, Conversion, }; SmallVector pairs; bool hasLabelMismatch = false; TupleMatcher(TupleType *tuple1, TupleType *tuple2) : tuple1(tuple1), tuple2(tuple2) {} bool match(MatchKind kind, ConstraintLocatorBuilder locator) { // FIXME: TuplePackMatcher should completely replace the non-variadic // case too eventually. if (containsPackExpansionType(tuple1) || containsPackExpansionType(tuple2)) { TuplePackMatcher matcher(tuple1, tuple2, isPackExpansionType); if (matcher.match()) return true; pairs = matcher.pairs; return false; } if (tuple1->getNumElements() != tuple2->getNumElements()) return true; switch (kind) { case MatchKind::Equality: return matchEquality(isInPatternMatchingContext(locator)); case MatchKind::Subtype: return matchSubtype(); case MatchKind::Conversion: return matchConversion(); } } private: bool matchEquality(bool inPatternMatchingContext) { for (unsigned i = 0, n = tuple1->getNumElements(); i != n; ++i) { const auto &elt1 = tuple1->getElement(i); const auto &elt2 = tuple2->getElement(i); if (inPatternMatchingContext) { // FIXME: The fact that this isn't symmetric is wrong since this logic // is called for bind and equal constraints... if (elt2.hasName() && elt1.getName() != elt2.getName()) return true; } else { // If the names don't match, we have a conflict. if (elt1.getName() != elt2.getName()) return true; } pairs.emplace_back(elt1.getType(), elt2.getType(), i, i); } return false; } bool matchSubtype() { for (unsigned i = 0, n = tuple1->getNumElements(); i != n; ++i) { const auto &elt1 = tuple1->getElement(i); const auto &elt2 = tuple2->getElement(i); // If the names don't match, we may have a conflict. if (elt1.getName() != elt2.getName()) { // Make sure that this name isn't used at some other position. if (elt2.hasName() && tuple1->getNamedElementId(elt2.getName()) != -1) return true; // If both elements have names and they mismatch, make a note of it // so we can emit a warning. if (elt1.hasName() && elt2.hasName()) hasLabelMismatch = true; } pairs.emplace_back(elt1.getType(), elt2.getType(), i, i); } return false; } bool matchConversion() { SmallVector sources; if (computeTupleShuffle(tuple1, tuple2, sources)) return true; for (unsigned idx2 = 0, n = sources.size(); idx2 != n; ++idx2) { unsigned idx1 = sources[idx2]; auto lhs = tuple1->getElementType(idx1); auto rhs = tuple2->getElementType(idx2); pairs.emplace_back(lhs, rhs, idx1, idx2); } return false; } }; } ConstraintSystem::TypeMatchResult ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { using TupleMatchKind = TupleMatcher::MatchKind; ConstraintKind subkind; TupleMatchKind matchKind; switch (kind) { case ConstraintKind::Bind: case ConstraintKind::Equal: { subkind = kind; matchKind = TupleMatchKind::Equality; break; } // NOTE: It was probably a mistake that BindToPointerType is handled like // Subtype; this was implicit in the old structure of the code due to bogus // use of operator<= on enum cases. case ConstraintKind::Subtype: case ConstraintKind::BindToPointerType: { subkind = kind; matchKind = TupleMatchKind::Subtype; break; } case ConstraintKind::Conversion: case ConstraintKind::ArgumentConversion: case ConstraintKind::OperatorArgumentConversion: { subkind = ConstraintKind::Conversion; matchKind = TupleMatchKind::Conversion; break; } case ConstraintKind::BindParam: case ConstraintKind::ApplicableFunction: case ConstraintKind::DynamicCallableApplicableFunction: case ConstraintKind::BindOverload: case ConstraintKind::CheckedCast: case ConstraintKind::SubclassOf: case ConstraintKind::NonisolatedConformsTo: case ConstraintKind::ConformsTo: case ConstraintKind::TransitivelyConformsTo: case ConstraintKind::Defaultable: case ConstraintKind::Disjunction: case ConstraintKind::Conjunction: case ConstraintKind::DynamicTypeOf: case ConstraintKind::EscapableFunctionOf: case ConstraintKind::OpenedExistentialOf: case ConstraintKind::KeyPath: case ConstraintKind::KeyPathApplication: case ConstraintKind::LiteralConformsTo: case ConstraintKind::OptionalObject: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: case ConstraintKind::ValueWitness: case ConstraintKind::BridgingConversion: case ConstraintKind::OneWayEqual: case ConstraintKind::FallbackType: case ConstraintKind::UnresolvedMemberChainBase: case ConstraintKind::PropertyWrapper: case ConstraintKind::SyntacticElement: case ConstraintKind::BindTupleOfFunctionParams: case ConstraintKind::PackElementOf: case ConstraintKind::ShapeOf: case ConstraintKind::ExplicitGenericArguments: case ConstraintKind::SameShape: case ConstraintKind::MaterializePackExpansion: case ConstraintKind::LValueObject: llvm_unreachable("Bad constraint kind in matchTupleTypes()"); } TupleMatcher matcher(tuple1, tuple2); if (matcher.match(matchKind, locator)) return getTypeMatchFailure(locator); if (matcher.hasLabelMismatch) { // If we had a label mismatch, emit a warning. This is something we // shouldn't permit, as it's more permissive than what a conversion would // allow. Ideally we'd turn this into an error in Swift 6 mode. recordFix(AllowTupleLabelMismatch::create(*this, tuple1, tuple2, getConstraintLocator(locator))); } TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); for (auto pair : matcher.pairs) { auto result = matchTypes(pair.lhs, pair.rhs, subkind, subflags, locator.withPathElement( LocatorPathElt::TupleElement(pair.lhsIdx))); if (result.isFailure()) return result; } return getTypeMatchSuccess(); } ConstraintSystem::TypeMatchResult ConstraintSystem::matchPackTypes(PackType *pack1, PackType *pack2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); PackMatcher matcher(pack1->getElementTypes(), pack2->getElementTypes(), getASTContext(), isPackExpansionType); if (matcher.match()) return getTypeMatchFailure(locator); for (auto pair : matcher.pairs) { auto result = matchTypes(pair.lhs, pair.rhs, kind, subflags, locator.withPathElement( LocatorPathElt::PackElement(pair.lhsIdx))); if (result.isFailure()) return result; } return getTypeMatchSuccess(); } /// Utility function used when matching a pack expansion type against a /// pack type. /// /// Takes a pattern type and an original pack type, and returns an instantiated /// pack type. The original pack type is then matched against the instantiated /// pack type. /// /// As a side effect, it binds each pack type variable occurring in the pattern /// type to a new pack with the same shape as the original pack, but where the /// elements are fresh type variables. /// /// The instantiated pack has the same shape as the original pack, where the /// ith element is the pattern type with each pack type variable replaced by the /// ith element of its binding. /// /// For example, given the pattern Foo<$T0> and the original pack /// {Foo, Foo...}, we're going to bind /// /// $T0 := {$T1, $T2} /// /// And return the new pack {Foo<$T1>, Foo<$T2>...}. /// /// The caller will then match the original pack type against the instantiated /// pack type, which will recover the bindings: /// /// $T1 := Int /// $T2 := String /// static PackType *replaceTypeVariablesWithFreshPacks(ConstraintSystem &cs, Type pattern, PackType *pack, ConstraintLocatorBuilder locator) { llvm::SmallSetVector typeVarSet; llvm::MapVector> typeVars; pattern->walkPackReferences([&](Type t) { if (auto *typeVar = t->getAs()) { if (typeVar->getImpl().canBindToPack()) typeVarSet.insert(typeVar); } return false; }); if (typeVarSet.empty()) return nullptr; auto *loc = cs.getConstraintLocator(locator); // For each pack type variable occurring in the pattern type, compute a // binding pack type comprised of fresh type variables. for (auto *typeVar : typeVarSet) { auto &freshTypeVars = typeVars[typeVar]; for (unsigned i = 0, e = pack->getNumElements(); i < e; ++i) { auto *packExpansionElt = pack->getElementType(i)->getAs(); // Preserve the pack expansion structure of the original pack. If the ith // element was a pack expansion type, create a new pack expansion type // wrapping a pack type variable. Otherwise, create a new scalar // type variable. // // FIXME: Other TVO_* flags for type variables? auto elementLoc = cs.getConstraintLocator(loc, LocatorPathElt::PackElement(freshTypeVars.size())); if (packExpansionElt != nullptr) { auto *freshTypeVar = cs.createTypeVariable( elementLoc, TVO_CanBindToPack | (typeVar->getImpl().canBindToHole() ? TVO_CanBindToHole : 0)); freshTypeVars.push_back(PackExpansionType::get( freshTypeVar, packExpansionElt->getCountType())); } else { freshTypeVars.push_back(cs.createTypeVariable( elementLoc, typeVar->getImpl().canBindToHole() ? TVO_CanBindToHole : 0)); } } } SmallVector elts; // For each element of the original pack type, instantiate the pattern type by // replacing each pack type variable with the corresponding element of the // pack type variable's binding pack. for (unsigned i = 0, e = pack->getNumElements(); i < e; ++i) { auto *packExpansionElt = pack->getElementType(i)->getAs(); auto instantiatedPattern = pattern.transformRec([&](Type t) -> std::optional { if (isPackExpansionType(t)) return t; if (auto *typeVar = t->getAs()) { if (typeVar->getImpl().canBindToPack()) { auto found = typeVars.find(typeVar); assert(found != typeVars.end()); // The ith element of the binding pack is either a scalar type variable // or a pack expansion type wrapping a pack type variable. auto projectedType = (found->second)[i]; if (packExpansionElt != nullptr) { projectedType = projectedType->castTo() ->getPatternType(); assert(projectedType->castTo() ->getImpl().canBindToPack()); } else { assert(!projectedType->castTo() ->getImpl().canBindToPack()); } return projectedType; } } return std::nullopt; }); if (packExpansionElt != nullptr) { elts.push_back(PackExpansionType::get(instantiatedPattern, packExpansionElt->getCountType())); } else { elts.push_back(instantiatedPattern); } } auto &ctx = cs.getASTContext(); // Bind each pack type variable occurring in the pattern type to its // binding pack that was constructed above. for (const auto &pair : typeVars) { cs.addConstraint(ConstraintKind::Bind, pair.first, PackType::get(ctx, pair.second), locator); } // Construct the instantiated pack type. return PackType::get(cs.getASTContext(), elts); } ConstraintSystem::TypeMatchResult ConstraintSystem::matchPackExpansionTypes(PackExpansionType *expansion1, PackExpansionType *expansion2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto shapeLocator = locator.withPathElement(ConstraintLocator::PackShape); // The count types of two pack expansion types must have the same shape. addConstraint(ConstraintKind::SameShape, expansion1->getCountType(), expansion2->getCountType(), shapeLocator); auto pattern1 = expansion1->getPatternType(); auto pattern2 = expansion2->getPatternType(); if (shouldAttemptFixes()) { // If pack expansion types have different shapes, let's not attempt // to match their pattern types to avoid producing any extra errors // caused by shape differences. if (hasFixFor(getConstraintLocator(shapeLocator))) { recordAnyTypeVarAsPotentialHole(pattern1); recordAnyTypeVarAsPotentialHole(pattern2); return getTypeMatchSuccess(); } } auto *const pack1 = pattern1->getAs(); auto *const pack2 = pattern2->getAs(); // If both sides are expanded or neither side is, proceed to matching them // directly. // Otherwise, we have something like `Foo<$T0>` vs. // `Pack{Foo, Foo}` or vice versa. // We're going to bind `$T0` to `Pack{Int, String}` and unfold `Foo<$T0>` into // `Pack{Foo<$T3>, Foo<$T4>} first. if ((bool)pack1 != (bool)pack2) { if (pack1) { pattern2 = replaceTypeVariablesWithFreshPacks(*this, pattern2, pack1, locator); } else { pattern1 = replaceTypeVariablesWithFreshPacks(*this, pattern1, pack2, locator); } if (!(pattern1 && pattern2)) { return getTypeMatchFailure(locator); } } // Continue matching. return matchTypes(pattern1, pattern2, kind, flags, locator); } /// Check where a representation is a subtype of another. /// /// The subtype relationship is defined as: /// 1. any representation R is a sub-type of itself. /// 2. a thin representation is a subtype of any other representation. /// 3. a thick representation is a subtype of any other thick representation. /// /// For example, since `@convention(c)` is a thin representation, and /// `@convention(swift)` is a thick representation, /// `@convention(c) (A) -> B` is a sub-type of `(A) -> B`. /// /// NOTE: Unlike typical subtyping relationships, this is not anti-symmetric. /// For example, @convention(c) and @convention(thin) are subtypes of each other /// but not equal. static bool isSubtypeOf(FunctionTypeRepresentation potentialSubRepr, FunctionTypeRepresentation potentialSuperRepr) { return (potentialSubRepr == potentialSuperRepr) || isThinRepresentation(potentialSubRepr) || isThickRepresentation(potentialSuperRepr); } /// Returns true if `constraint extInfo1 extInfo2` is satisfied. static bool matchFunctionRepresentations(FunctionType::ExtInfo einfo1, FunctionType::ExtInfo einfo2, ConstraintKind kind, ConstraintSystemOptions options) { auto rep1 = einfo1.getRepresentation(); auto rep2 = einfo2.getRepresentation(); bool clangTypeMismatch = (options.contains(ConstraintSystemFlags::UseClangFunctionTypes) && (einfo1.getClangTypeInfo() != einfo2.getClangTypeInfo())); switch (kind) { case ConstraintKind::Bind: case ConstraintKind::BindParam: case ConstraintKind::BindToPointerType: case ConstraintKind::Equal: return (rep1 == rep2) && !clangTypeMismatch; case ConstraintKind::Subtype: { // Breakdown of cases: // 1. isSubtypeOf(rep1, rep2) == false (hence rep1 != rep2): // In this case, this function will return false, indicating that we // can't convert. E.g. you can't convert from @convention(swift) to // @convention(c). // 2. isSubtypeOf(rep1, rep2) == true and rep1 != rep2: // In this case, this function will return true, indicating that we // can convert, because the Clang type doesn't matter when converting // between different representations. E.g. it is okay to convert from // @convention(c) (regardless of cType) to @convention(swift). // 3. isSubtypeOf(rep1, rep2) == true and rep1 == rep2: // In this case, the function returns !clangTypeMismatch, as we forbid // conversions between @convention(c) functions with different cTypes. return isSubtypeOf(rep1, rep2) && ((rep1 != rep2) || !clangTypeMismatch); } // [NOTE: diagnose-swift-to-c-convention-change]: @convention(swift) -> // @convention(c) conversions are permitted only in certain cases. // // var w = 3; func f() { print(w) }; func g(_ : @convention(c) () -> ()) {} // g(f); // OK // let h = f as @convention(c) () -> (); g(h) // OK // let k = f; g(k) // error // func m() { let x = 0; g({ print(x) }) } // error // func n() { let y = 0; func p() { }; g(p); } // OK // func q() { let z = 0; func r() { print(z) }; g(r); } // error // // Since checking for disallowed cases requires access to captures, // it is simpler to defer diagnosing (to CSApply/SILGen) and return true here. case ConstraintKind::Conversion: case ConstraintKind::ArgumentConversion: case ConstraintKind::OperatorArgumentConversion: // For now, forbid conversion if representations match but cTypes differ. // // let f : @convention(c, cType: "id (*)(void) __attribute__((ns_returns_retained))") // () -> AnyObject = ... // let _ : @convention(c, cType: "id (*)(void)") // () -> AnyObject = f // error // let g : @convention(c, cType: "void (*)(void *)") // (OpaquePointer?) -> () = ... // let _ : @convention(c, cType: "void (*)(MyCtx *)") // (OpaquePointer?) -> () = g // error if ((rep1 == rep2) && clangTypeMismatch) { return false; } return true; case ConstraintKind::BridgingConversion: case ConstraintKind::ApplicableFunction: case ConstraintKind::DynamicCallableApplicableFunction: case ConstraintKind::BindOverload: case ConstraintKind::CheckedCast: case ConstraintKind::SubclassOf: case ConstraintKind::NonisolatedConformsTo: case ConstraintKind::ConformsTo: case ConstraintKind::TransitivelyConformsTo: case ConstraintKind::Defaultable: case ConstraintKind::Disjunction: case ConstraintKind::Conjunction: case ConstraintKind::DynamicTypeOf: case ConstraintKind::EscapableFunctionOf: case ConstraintKind::OpenedExistentialOf: case ConstraintKind::KeyPath: case ConstraintKind::KeyPathApplication: case ConstraintKind::LiteralConformsTo: case ConstraintKind::OptionalObject: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: case ConstraintKind::ValueWitness: case ConstraintKind::OneWayEqual: case ConstraintKind::FallbackType: case ConstraintKind::UnresolvedMemberChainBase: case ConstraintKind::PropertyWrapper: case ConstraintKind::SyntacticElement: case ConstraintKind::BindTupleOfFunctionParams: case ConstraintKind::PackElementOf: case ConstraintKind::ShapeOf: case ConstraintKind::ExplicitGenericArguments: case ConstraintKind::SameShape: case ConstraintKind::MaterializePackExpansion: case ConstraintKind::LValueObject: return true; } llvm_unreachable("Unhandled ConstraintKind in switch."); } /// Check whether given parameter list represents a single tuple /// or type variable which could be later resolved to tuple. /// This is useful for SE-0110 related fixes in `matchFunctionTypes`. static bool isSingleTupleParam(ASTContext &ctx, ArrayRef params) { if (params.size() != 1) return false; const auto ¶m = params.front(); if ((param.isVariadic() || isPackExpansionType(param.getPlainType())) || param.isInOut() || param.hasLabel() || param.isIsolated()) return false; auto paramType = param.getPlainType(); // Support following case which was allowed until 5: // // func bar(_: (Int, Int) -> Void) {} // let foo: ((Int, Int)?) -> Void = { _ in } // // bar(foo) // Ok if (!ctx.isSwiftVersionAtLeast(5)) paramType = paramType->lookThroughAllOptionalTypes(); // Parameter type should either a tuple or something that can become a // tuple later on. return (paramType->is() || paramType->isTypeVariableOrMember()); } static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1, Type type2, ASTNode anchor, ArrayRef path); static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1, Type type2, ConstraintLocatorBuilder locator) { SmallVector path; auto anchor = locator.getLocatorParts(path); return fixRequirementFailure(cs, type1, type2, anchor, path); } static unsigned assessRequirementFailureImpact(ConstraintSystem &cs, Type requirementType, ConstraintLocatorBuilder locator) { assert(requirementType); unsigned impact = 1; auto anchor = locator.getAnchor(); if (!anchor) return impact; // If this requirement is associated with a member reference and it // was possible to check it before overload choice is bound, that means // types came from the context (most likely Self, or associated type(s)) // and failing this constraint makes member unrelated/inaccessible, so // the impact has to be adjusted accordingly in order for this fix not to // interfere with other overload choices. // // struct S {} // extension S where T == AnyObject { func foo() {} } // // func bar(_ s: S) { s.foo() } // // In this case `foo` is only accessible if T == `AnyObject`, which makes // fix for same-type requirement higher impact vs. requirement associated // with method itself e.g. `func foo() -> U where U : P {}` because // `foo` is accessible from any `S` regardless of what `T` is. // // Don't add this impact with the others, as we want to keep it consistent // across requirement failures to present the user with a choice. if (isExpr(anchor) || isExpr(anchor)) { auto *calleeLoc = cs.getCalleeLocator(cs.getConstraintLocator(locator)); if (!cs.findSelectedOverloadFor(calleeLoc)) return 10; } if (auto *UDE = getAsExpr(anchor)) { if (isResultBuilderMethodReference(cs.getASTContext(), UDE)) return 12; } auto resolvedTy = cs.simplifyType(requirementType); // Increase the impact of a conformance fix for generic parameters on // operators where such conformance failures are not as important as argument // mismatches or contextual failures. if (auto *ODRE = getAsExpr(anchor)) { if (locator.isForRequirement(RequirementKind::Conformance) && resolvedTy->is() && ODRE->isForOperator()) { ++impact; } } if (locator.isForRequirement(RequirementKind::Conformance)) { // Increase the impact of a conformance fix for a standard library // or foundation type, as it's unlikely to be a good suggestion. { if (resolvedTy->isStdlibType()) { impact += 2; } if (auto *NTD = resolvedTy->getAnyNominal()) { if (getKnownFoundationEntity(NTD->getNameStr())) impact += 2; } } // Also do the same for the builtin compiler types Any and AnyObject, but // bump the impact even higher as they cannot conform to protocols at all. if (resolvedTy->isAny() || resolvedTy->isAnyObject()) impact += 4; } // If this requirement is associated with an overload choice let's // tie impact to how many times this requirement type is mentioned. if (auto *ODRE = getAsExpr(anchor)) { if (auto *typeVar = requirementType->getAs()) { unsigned choiceImpact = 0; if (auto choice = cs.findSelectedOverloadFor(ODRE)) { choice->adjustedOpenedType.visit([&](Type type) { if (type->isEqual(typeVar)) ++choiceImpact; }); } // If the type is used multiple times in the signature, increase the // impact for every additional use. if (choiceImpact > 1) impact += choiceImpact - 1; } } // If this requirement is associated with a call that is itself // incorrect, let's increase impact to indicate that this failure // has a compounding effect on viability of the overload choice it // comes from. if (locator.endsWith()) { if (auto *expr = getAsExpr(anchor)) { if (auto *call = getAsExpr(cs.getParentExpr(expr))) { if (call->getFn() == expr && llvm::any_of(cs.getFixes(), [&](const auto &fix) { return getAsExpr(fix->getAnchor()) == call; })) impact += 2; } } } return impact; } /// Attempt to fix missing arguments by introducing type variables /// and inferring their types from parameters. static bool fixMissingArguments(ConstraintSystem &cs, ASTNode anchor, SmallVectorImpl &args, ArrayRef params, unsigned numMissing, ConstraintLocatorBuilder locator) { assert(args.size() < params.size()); auto &ctx = cs.getASTContext(); // If there are N parameters but a single closure argument // (which might be anonymous), it's most likely used as a // tuple e.g. `$0.0`. std::optional argumentTuple; if (isSingleTupleParam(ctx, args)) { auto argType = args.back().getPlainType(); // Let's unpack argument tuple into N arguments, this corresponds // to something like `foo { (bar: (Int, Int)) in }` where `foo` // has a single parameter of type `(Int, Int) -> Void`. if (auto *tuple = argType->getAs()) { args.pop_back(); for (const auto &elt : tuple->getElements()) args.emplace_back(elt.getType(), elt.getName()); } else if (auto *typeVar = argType->getAs()) { auto isParam = [](const Expr *expr) { if (auto *DRE = dyn_cast(expr)) { if (auto *decl = DRE->getDecl()) return isa(decl); } return false; }; // Something like `foo { x in }` or `foo { $0 }` if (auto *closure = getAsExpr(anchor)) { cs.forEachExpr(closure, [&](Expr *expr) -> Expr * { if (auto *UDE = dyn_cast(expr)) { if (!isParam(UDE->getBase())) return expr; auto name = UDE->getName().getBaseIdentifier(); unsigned index = 0; if (!name.str().getAsInteger(10, index) || llvm::any_of(params, [&](const AnyFunctionType::Param ¶m) { return param.getLabel() == name; })) { argumentTuple.emplace(typeVar); args.pop_back(); return nullptr; } } return expr; }); } } } for (unsigned i = args.size(), n = params.size(); i != n; ++i) { auto *argLoc = cs.getConstraintLocator( anchor, LocatorPathElt::SynthesizedArgument(i)); args.push_back(params[i].withType( cs.createTypeVariable(argLoc, TVO_CanBindToNoEscape))); } SmallVector synthesizedArgs; synthesizedArgs.reserve(numMissing); for (unsigned i = args.size() - numMissing, n = args.size(); i != n; ++i) { synthesizedArgs.push_back(SynthesizedArg{i, args[i]}); } // Treat missing anonymous arguments as valid in closures containing the // code completion location, since they may have just not been written yet. if (cs.isForCodeCompletion()) { if (auto *closure = getAsExpr(anchor)) { if (cs.containsIDEInspectionTarget(closure) && (closure->hasAnonymousClosureVars() || (args.empty() && closure->getInLoc().isInvalid()))) return false; } } auto *fix = AddMissingArguments::create(cs, synthesizedArgs, cs.getConstraintLocator(locator)); if (cs.recordFix(fix)) return true; // If the argument was a single "tuple", let's bind newly // synthesized arguments to it. if (argumentTuple) { // We can ignore parameter flags here as we're imploding a tuple for a // simulated ((X, Y, Z)) -> R to (X, Y, Z) -> R conversion. As such, this is // similar to e.g { x, y, z in fn((x, y, z)) }. cs.addConstraint(ConstraintKind::Bind, *argumentTuple, FunctionType::composeTuple( ctx, args, ParameterFlagHandling::IgnoreNonEmpty), cs.getConstraintLocator(anchor)); } return false; } static bool fixExtraneousArguments(ConstraintSystem &cs, FunctionType *contextualType, ArrayRef args, int numExtraneous, ConstraintLocatorBuilder locator) { SmallVector, 4> extraneous; for (unsigned i = args.size() - numExtraneous, n = args.size(); i != n; ++i) { extraneous.push_back({i, args[i]}); if (auto *typeVar = args[i].getPlainType()->getAs()) { cs.recordPotentialHole(typeVar); } } return cs.recordFix( RemoveExtraneousArguments::create(cs, contextualType, extraneous, cs.getConstraintLocator(locator)), /*impact=*/numExtraneous * 2); } bool ConstraintSystem::hasPreconcurrencyCallee( ConstraintLocatorBuilder locator) { auto calleeLocator = getCalleeLocator(getConstraintLocator(locator)); auto calleeOverload = findSelectedOverloadFor(calleeLocator); if (!calleeOverload || !calleeOverload->choice.isDecl()) return false; return calleeOverload->choice.getDecl()->preconcurrency(); } /// Match the throwing specifier of the two function types. static ConstraintSystem::TypeMatchResult matchFunctionThrowing(ConstraintSystem &cs, FunctionType *func1, FunctionType *func2, ConstraintKind kind, ConstraintSystem::TypeMatchOptions flags, ConstraintLocatorBuilder locator) { // A function type that throws the error type E1 is a subtype of a function // that throws error type E2 when E1 is a subtype of E2. For the purpose // of this comparison, a non-throwing function has thrown error type 'Never', // and an untyped throwing function has thrown error type 'any Error'. Type thrownError1 = func1->getEffectiveThrownErrorTypeOrNever(); Type thrownError2 = func2->getEffectiveThrownErrorTypeOrNever(); if (!thrownError1 || !thrownError2) return cs.getTypeMatchSuccess(); switch (compareThrownErrorsForSubtyping(thrownError1, thrownError2, cs.DC)) { case ThrownErrorSubtyping::DropsThrows: { // We need to drop 'throws' to make this work. if (!cs.shouldAttemptFixes()) return cs.getTypeMatchFailure(locator); auto *fix = DropThrowsAttribute::create(cs, func1, func2, cs.getConstraintLocator(locator)); if (cs.recordFix(fix)) return cs.getTypeMatchFailure(locator); return cs.getTypeMatchSuccess(); } case ThrownErrorSubtyping::ExactMatch: return cs.getTypeMatchSuccess(); case ThrownErrorSubtyping::Subtype: // We know this is going to work, but we might still need to generate a // constraint if one of the error types involves type variables. if (thrownError1->hasTypeVariable() || thrownError2->hasTypeVariable()) { // Fall through to the dependent case. } else if (kind < ConstraintKind::Subtype) { // We aren't allowed to have a subtype, so fail here. return cs.getTypeMatchFailure(locator); } else { // We have a subtype. All set! return cs.getTypeMatchSuccess(); } LLVM_FALLTHROUGH; case ThrownErrorSubtyping::Dependent: { // The presence of type variables in the thrown error types require that // we generate a constraint to unify the thrown error types, so do so now. ConstraintKind subKind = (kind < ConstraintKind::Subtype) ? ConstraintKind::Equal : ConstraintKind::Subtype; const auto subflags = getDefaultDecompositionOptions(flags); auto result = cs.matchTypes( thrownError1, thrownError2, subKind, subflags, locator.withPathElement(LocatorPathElt::ThrownErrorType())); if (result == ConstraintSystem::SolutionKind::Error) return cs.getTypeMatchFailure(locator); return cs.getTypeMatchSuccess(); } case ThrownErrorSubtyping::Mismatch: { auto thrownErrorLocator = cs.getConstraintLocator( locator.withPathElement(LocatorPathElt::ThrownErrorType())); if (!cs.shouldAttemptFixes()) return cs.getTypeMatchFailure(thrownErrorLocator); auto *fix = IgnoreThrownErrorMismatch::create( cs, thrownError1, thrownError2, thrownErrorLocator); if (cs.recordFix(fix)) return cs.getTypeMatchFailure(thrownErrorLocator); return cs.getTypeMatchSuccess(); } } } static bool isWitnessMatching(ConstraintLocatorBuilder locator) { SmallVector path; (void) locator.getLocatorParts(path); return (path.size() == 1 && path[0].is()); } bool ConstraintSystem::matchFunctionIsolations(FunctionType *func1, FunctionType *func2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto isolation1 = func1->getIsolation(), isolation2 = func2->getIsolation(); // If we have a difference in isolation kind, we need a conversion. // Make sure that we're looking for a conversion, and increase the // function-conversion score to make sure this solution is worse than // an exact match. // FIXME: there may be a better way. see https://github.com/apple/swift/pull/62514 auto matchIfConversion = [&](bool isErasure = false) -> bool { // We generally require a conversion here, but allow some lassitude // if we're doing witness-matching. if (kind < ConstraintKind::Subtype && !(isErasure && isWitnessMatching(locator))) return false; increaseScore(SK_FunctionConversion, locator); return true; }; switch (isolation2.getKind()) { // Converting to a non-isolated type. case FunctionTypeIsolation::Kind::NonIsolated: switch (isolation1.getKind()) { // Exact match. case FunctionTypeIsolation::Kind::NonIsolated: return true; // A thunk is going to pass `nil` to the isolated parameter. case FunctionTypeIsolation::Kind::NonIsolatedCaller: return matchIfConversion(); // Erasing global-actor isolation to non-isolation can admit data // races; such violations are diagnosed by the actor isolation checker. // We deliberately do not allow actor isolation violations to influence // overload resolution to preserve the property that an expression can // be re-checked against a different isolation context for isolation // violations. // // This also applies to @isolated(any) because we want to be able to // decide that we contextually isolated to the function's dynamic // isolation. case FunctionTypeIsolation::Kind::GlobalActor: case FunctionTypeIsolation::Kind::Erased: return matchIfConversion(); // Parameter isolation is value-dependent and cannot be erased. case FunctionTypeIsolation::Kind::Parameter: return false; } llvm_unreachable("bad kind"); // Converting to a caller isolated async function type. case FunctionTypeIsolation::Kind::NonIsolatedCaller: switch (isolation1.getKind()) { // Exact match. case FunctionTypeIsolation::Kind::NonIsolatedCaller: return true; // Global actor: Thunk will hop to the global actor // and would ignore passed in isolation. // Erased: Just like global actor but would hop to // the isolation stored in the @isolated(any) function. case FunctionTypeIsolation::Kind::GlobalActor: case FunctionTypeIsolation::Kind::Erased: return matchIfConversion(); // In this case the isolation is dependent on a // specific actor passed in as the isolation parameter // and the thunk won't have it. case FunctionTypeIsolation::Kind::Parameter: return false; // For asynchronous: Thunk would hop the appropriate actor. // For synchronous: Thunk would call the function without // a hop. case FunctionTypeIsolation::Kind::NonIsolated: return matchIfConversion(); } llvm_unreachable("bad kind"); // Converting to a global-actor-isolated type. case FunctionTypeIsolation::Kind::GlobalActor: switch (isolation1.getKind()) { // Both types are global-actor-isolated. We *could* allow this as a // conversion even for different global actors if the destination type // is async, but we've decided we don't want to as a policy. case FunctionTypeIsolation::Kind::GlobalActor: { const auto subflags = getDefaultDecompositionOptions(flags); auto result = matchTypes( isolation1.getGlobalActorType(), isolation2.getGlobalActorType(), ConstraintKind::Equal, subflags, locator.withPathElement(LocatorPathElt::GlobalActorType())); return result != SolutionKind::Error; } // Adding global actor isolation to a non-isolated function is fine, // whether synchronous or asynchronous. case FunctionTypeIsolation::Kind::NonIsolated: return matchIfConversion(); // A thunk is going to pass in an instance of a global actor // to the isolated parameter. case FunctionTypeIsolation::Kind::NonIsolatedCaller: return matchIfConversion(); // Parameter isolation cannot be altered in the same way. case FunctionTypeIsolation::Kind::Parameter: return false; // Don't allow dynamically-isolated function types to convert to // any specific isolation for the same policy reasons that we don't // want to allow global-actors to change. case FunctionTypeIsolation::Kind::Erased: return false; } llvm_unreachable("bad kind"); // Converting to a parameter-isolated type. case FunctionTypeIsolation::Kind::Parameter: switch (isolation1.getKind()) { // Exact match. We'll check that the isolated parameters match up later, // when we're looking at the parameters. case FunctionTypeIsolation::Kind::Parameter: return true; // Adding global actor isolation to a non-isolated function is fine, // whether synchronous or asynchronous. case FunctionTypeIsolation::Kind::NonIsolated: case FunctionTypeIsolation::Kind::GlobalActor: return matchIfConversion(); // A thunk is going to forward the isolation. case FunctionTypeIsolation::Kind::NonIsolatedCaller: return matchIfConversion(); // Don't allow dynamically-isolated function types to convert to // any specific isolation for the same policy reasons that we don't // want to allow global-actors to change. case FunctionTypeIsolation::Kind::Erased: return false; } llvm_unreachable("bad kind"); case FunctionTypeIsolation::Kind::Erased: switch (isolation1.getKind()) { // Exact match. case FunctionTypeIsolation::Kind::Erased: return true; // We can statically erase any kind of static isolation to dynamic // isolation as a conversion. case FunctionTypeIsolation::Kind::NonIsolated: case FunctionTypeIsolation::Kind::GlobalActor: return matchIfConversion(/*erasure*/ true); // It's not possible to form a thunk for this case because // we don't know what to pass to the isolated parameter. case FunctionTypeIsolation::Kind::NonIsolatedCaller: return false; // Parameter isolation is value-dependent and can't be erased in the // abstract, though. We need to be able to recover the isolation from // a value. case FunctionTypeIsolation::Kind::Parameter: return false; } llvm_unreachable("bad kind"); } llvm_unreachable("bad kind"); } ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { // Match the 'throws' effect. TypeMatchResult throwsResult = matchFunctionThrowing(*this, func1, func2, kind, flags, locator); if (throwsResult.isFailure()) return throwsResult; // A synchronous function can be a subtype of an 'async' function. if (func1->isAsync() != func2->isAsync()) { // Cannot drop 'async'. if (func1->isAsync() || kind < ConstraintKind::Subtype) { if (!shouldAttemptFixes()) return getTypeMatchFailure(locator); auto *fix = DropAsyncAttribute::create(*this, func1, func2, getConstraintLocator(locator)); if (recordFix(fix)) return getTypeMatchFailure(locator); } bool forClosureInArgumentPosition = locator.endsWith() && isa(locator.trySimplifyToExpr()); // Since it's possible to infer `async` from the body of a // closure, score for sync -> async mismatch is increased // while solver is matching arguments to parameters to // indicate than solution with such a mismatch is always // worse than one with synchronous functions on both sides. if (!forClosureInArgumentPosition) increaseScore(SK_SyncInAsync, locator); } // A @Sendable function can be a subtype of a non-@Sendable function. if (func1->isSendable() != func2->isSendable()) { // Cannot add '@Sendable'. if (func2->isSendable() || kind < ConstraintKind::Subtype) { if (AddSendableAttribute::attempt(*this, kind, func1, func2, locator)) return getTypeMatchFailure(locator); } } // A non-@noescape function type can be a subtype of a @noescape function // type. if (func1->isNoEscape() != func2->isNoEscape() && (func1->isNoEscape() || kind < ConstraintKind::Subtype)) { if (!shouldAttemptFixes()) return getTypeMatchFailure(locator); auto *fix = MarkExplicitlyEscaping::create(*this, func1, func2, getConstraintLocator(locator)); if (recordFix(fix)) return getTypeMatchFailure(locator); } // () -> sending T can be a subtype of () -> T... but not vis-a-versa. if (func1->hasSendingResult() != func2->hasSendingResult() && (!func1->hasSendingResult() || kind < ConstraintKind::Subtype)) { auto *fix = AllowSendingMismatch::create(*this, func1, func2, getConstraintLocator(locator)); if (recordFix(fix)) return getTypeMatchFailure(locator); } if (!matchFunctionIsolations(func1, func2, kind, flags, locator)) return getTypeMatchFailure(locator); // A function with a lifetime dependency in a generic context is equivalent to // one without that lifetime dependency when the substituted type is // Escapable. // // TODO: There should also be a subtype relationship from less-constrained to // more-constrained lifetime dependencies. if (func1->getLifetimeDependencies() != func2->getLifetimeDependencies()) { auto escapable = getASTContext().getProtocol(KnownProtocolKind::Escapable) ->getDeclaredType(); for (auto &fromDep : func1->getLifetimeDependencies()) { auto toDep = func2->getLifetimeDependenceFor(fromDep.getTargetIndex()); if (toDep) { // If a dependency is present for the same target in both types, then // the dependency must match. if (fromDep != *toDep) { return getTypeMatchFailure(locator); } continue; } // If the dependency is absent from the destination type, constrain the // corresponding parameter or result in the source type to be Escapable. if (fromDep.getTargetIndex() == func1->getParams().size()) { // Result dependency. addConstraint(ConstraintKind::ConformsTo, func1->getResult(), escapable, locator); } else { // Parameter dependency. addConstraint(ConstraintKind::ConformsTo, func1->getParams()[fromDep.getTargetIndex()].getPlainType(), escapable, locator); } } } // To contextual type increase the score to avoid ambiguity when solver can // find more than one viable binding different only in representation e.g. // let _: (@convention(block) () -> Void)? = Bool.random() ? nil : {} // so same representation should be always favored. auto loc = getConstraintLocator(locator); if (loc->findLast() && func1->getRepresentation() != func2->getRepresentation()) { increaseScore(SK_FunctionConversion, locator); } if (!matchFunctionRepresentations(func1->getExtInfo(), func2->getExtInfo(), kind, Options)) { return getTypeMatchFailure(locator); } // Determine how we match up the input/result types. ConstraintKind subKind; switch (kind) { case ConstraintKind::Bind: case ConstraintKind::BindParam: case ConstraintKind::BindToPointerType: case ConstraintKind::Equal: subKind = kind; break; case ConstraintKind::Subtype: case ConstraintKind::Conversion: case ConstraintKind::ArgumentConversion: case ConstraintKind::OperatorArgumentConversion: subKind = ConstraintKind::Subtype; break; case ConstraintKind::ApplicableFunction: case ConstraintKind::DynamicCallableApplicableFunction: case ConstraintKind::BindOverload: case ConstraintKind::CheckedCast: case ConstraintKind::SubclassOf: case ConstraintKind::NonisolatedConformsTo: case ConstraintKind::ConformsTo: case ConstraintKind::TransitivelyConformsTo: case ConstraintKind::Defaultable: case ConstraintKind::Disjunction: case ConstraintKind::Conjunction: case ConstraintKind::DynamicTypeOf: case ConstraintKind::EscapableFunctionOf: case ConstraintKind::OpenedExistentialOf: case ConstraintKind::KeyPath: case ConstraintKind::KeyPathApplication: case ConstraintKind::LiteralConformsTo: case ConstraintKind::OptionalObject: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: case ConstraintKind::ValueWitness: case ConstraintKind::BridgingConversion: case ConstraintKind::OneWayEqual: case ConstraintKind::FallbackType: case ConstraintKind::UnresolvedMemberChainBase: case ConstraintKind::PropertyWrapper: case ConstraintKind::SyntacticElement: case ConstraintKind::BindTupleOfFunctionParams: case ConstraintKind::PackElementOf: case ConstraintKind::ShapeOf: case ConstraintKind::ExplicitGenericArguments: case ConstraintKind::SameShape: case ConstraintKind::MaterializePackExpansion: case ConstraintKind::LValueObject: llvm_unreachable("Not a relational constraint"); } // Input types can be contravariant (or equal). auto argumentLocator = locator.withPathElement(ConstraintLocator::FunctionArgument); TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); SmallVector func1Params; func1Params.append(func1->getParams().begin(), func1->getParams().end()); SmallVector func2Params; func2Params.append(func2->getParams().begin(), func2->getParams().end()); // Support conversion from `nonisolated(nonsending)` to a function type // with an isolated parameter. if (subKind == ConstraintKind::Subtype && func1->getIsolation().isNonIsolatedCaller() && func2->getIsolation().isParameter()) { // `nonisolated(nonsending)` function gets an implicit isolation parameter // introduced during SILGen and thunk is going to forward an isolation from // the caller to it. // Let's remove the isolated parameter from consideration, function // types have to match on everything else. llvm::erase_if(func2Params, [](const AnyFunctionType::Param ¶m) { return param.isIsolated(); }); } // Add a very narrow exception to SE-0110 by allowing functions that // take multiple arguments to be passed as an argument in places // that expect a function that takes a single tuple (of the same // arity); auto canImplodeParams = [&](ArrayRef params, const FunctionType *destFn) { if (params.size() == 1) return false; // We do not support imploding into a @differentiable function. if (destFn->isDifferentiable()) return false; for (auto ¶m : params) { // We generally cannot handle parameter flags, though we can carve out an // exception for ownership flags such as __owned, which we can thunk, and // flags that can freely dropped from a function type such as // @_nonEphemeral. Note that @noDerivative can also be freely dropped, as // we've already ensured that the destination function is not // @differentiable. auto flags = param.getParameterFlags(); flags = flags.withOwnershipSpecifier( param.isInOut() ? ParamSpecifier::InOut : ParamSpecifier::Default); flags = flags.withNonEphemeral(false) .withNoDerivative(false); if (!flags.isNone()) return false; } return true; }; auto implodeParams = [&](SmallVectorImpl ¶ms) { // Form an imploded tuple type, dropping the parameter flags as although // canImplodeParams makes sure we're not dealing with vargs, inout, etc, // we may still have e.g ownership flags left over, which we can drop. auto input = AnyFunctionType::composeTuple( getASTContext(), params, ParameterFlagHandling::IgnoreNonEmpty); params.clear(); // If fixes are disabled let's do an easy thing and implode // tuple directly into parameters list. if (!shouldAttemptFixes()) { params.emplace_back(input); return; } // Synthesize new argument and bind it to tuple formed from existing // arguments, this makes it easier to diagnose cases where we attempt // a single tuple element formed when no arguments were present. auto argLoc = argumentLocator.withPathElement( LocatorPathElt::SynthesizedArgument(0)); auto *typeVar = createTypeVariable(getConstraintLocator(argLoc), TVO_CanBindToNoEscape); params.emplace_back(typeVar); assignFixedType(typeVar, input); }; { SmallVector path; locator.getLocatorParts(path); // Find the last path element, skipping OptionalInjection elements // so that we allow this exception in cases of optional injection. auto last = std::find_if( path.rbegin(), path.rend(), [](LocatorPathElt &elt) -> bool { return elt.getKind() != ConstraintLocator::OptionalInjection; }); auto &ctx = getASTContext(); if (last != path.rend()) { if (last->getKind() == ConstraintLocator::ApplyArgToParam) { if (isSingleTupleParam(ctx, func2Params) && canImplodeParams(func1Params, /*destFn*/ func2)) { implodeParams(func1Params); increaseScore(SK_FunctionConversion, locator); } else if (!ctx.isSwiftVersionAtLeast(5) && isSingleTupleParam(ctx, func1Params) && canImplodeParams(func2Params, /*destFn*/ func1)) { auto *simplified = locator.trySimplifyToExpr(); // We somehow let tuple unsplatting function conversions // through in some cases in Swift 4, so let's let that // continue to work, but only for Swift 4. if (simplified && (isa(simplified) || isa(simplified) || isa(simplified))) { implodeParams(func2Params); increaseScore(SK_FunctionConversion, locator); } } } else if (last->is() && isa( last->castTo() .getPattern())) { // A single paren pattern becomes a labeled tuple pattern // e.g. `case .test(let value):` should be able to match // `case test(result: Int)`. Note that it also means that: // `cast test(result: (String, Int))` would be matched against // e.g. `case .test((let x, let y))` but that fails during // pattern coercion (behavior consistent with what happens in // `TypeCheckPattern`). if (func1Params.size() == 1 && !func1Params.front().hasLabel() && func2Params.size() == 1 && func2Params.front().hasLabel()) { auto param = func1Params.front(); auto label = func2Params.front().getLabel(); auto labeledParam = FunctionType::Param(param.getPlainType(), label, param.getParameterFlags()); func1Params.clear(); func1Params.push_back(labeledParam); } // Consider following example: // // enum E { // case foo((x: Int, y: Int)) // case bar(x: Int, y: Int) // } // // func test(e: E) { // if case .foo(let x, let y) = e {} // if case .bar(let tuple) = e {} // } // // Both of `if case` expressions have to be supported: // // 1. `case .foo(let x, let y) = e` allows a single tuple // parameter to be "destructured" into multiple arguments. // // 2. `case .bar(let tuple) = e` allows to match multiple // parameters with a single tuple argument. if (isSingleTupleParam(ctx, func1Params) && canImplodeParams(func2Params, /*destFn*/ func1)) { implodeParams(func2Params); increaseScore(SK_FunctionConversion, locator); } else if (isSingleTupleParam(ctx, func2Params) && canImplodeParams(func1Params, /*destFn*/ func2)) { implodeParams(func1Params); increaseScore(SK_FunctionConversion, locator); } } } if (shouldAttemptFixes()) { auto *anchor = locator.trySimplifyToExpr(); if (isa_and_nonnull(anchor) && isSingleTupleParam(ctx, func2Params) && canImplodeParams(func1Params, /*destFn*/ func2)) { auto *fix = AllowClosureParamDestructuring::create( *this, func2, getConstraintLocator(anchor)); if (recordFix(fix)) return getTypeMatchFailure(argumentLocator); implodeParams(func1Params); } } } // https://github.com/apple/swift/issues/49345 // Add a super-narrow hack to allow '(()) -> T' to be passed in place // of '() -> T'. if (getASTContext().isSwiftVersionAtLeast(4) && !getASTContext().isSwiftVersionAtLeast(5)) { SmallVector path; locator.getLocatorParts(path); // Find the last path element, skipping GenericArgument elements // so that we allow this exception in cases of optional types, and // skipping OptionalInjection elements so that we allow this // exception in cases of optional injection. auto last = std::find_if( path.rbegin(), path.rend(), [](LocatorPathElt &elt) -> bool { return elt.getKind() != ConstraintLocator::GenericArgument && elt.getKind() != ConstraintLocator::OptionalInjection; }); if (last != path.rend()) { if (last->getKind() == ConstraintLocator::ApplyArgToParam) { if (isSingleTupleParam(getASTContext(), func1Params) && func1Params[0].getOldType()->isVoid()) { if (func2Params.empty()) { func2Params.emplace_back(getASTContext().TheEmptyTupleType); } } } } } // FIXME: ParamPackMatcher should completely replace the non-variadic // case too eventually. if (containsPackExpansionType(func1Params) || containsPackExpansionType(func2Params)) { ParamPackMatcher matcher(func1Params, func2Params, getASTContext(), isPackExpansionType); if (matcher.match()) return getTypeMatchFailure(locator); for (auto pair : matcher.pairs) { // Compare the parameter types, taking contravariance into account. auto result = matchTypes(pair.rhs, pair.lhs, subKind, subflags, (func1Params.size() == 1 ? argumentLocator : argumentLocator.withPathElement( LocatorPathElt::TupleElement(pair.lhsIdx)))); if (result.isFailure()) return result; } } else { int diff = func1Params.size() - func2Params.size(); if (diff != 0) { if (!shouldAttemptFixes()) return getTypeMatchFailure(argumentLocator); auto *loc = getConstraintLocator(locator); // If this is conversion between optional (or IUO) parameter // and argument, let's drop the last path element so locator // could be simplified down to an argument expression. // // func foo(_: ((Int, Int) -> Void)?) {} // _ = foo { _ in } <- missing second closure parameter. if (loc->isLastElement()) { auto path = loc->getPath(); loc = getConstraintLocator(loc->getAnchor(), path.drop_back()); } auto anchor = simplifyLocatorToAnchor(loc); if (!anchor) return getTypeMatchFailure(argumentLocator); // The param diff is in a function type coercion context // // func fn(_: Int) {} // let i: Int = 0 // (fn as (Int, Int) -> Void)(i, i) // // Since we are not in a function argument application, simply record // a function type mismatch instead of an argument fix. // Except for when a closure is a subexpr because closure expr parameters // syntax can be added or removed by missing/extraneous arguments fix. if (loc->isForCoercion() && !isExpr(anchor)) { auto *fix = ContextualMismatch::create(*this, func1, func2, loc); if (recordFix(fix)) return getTypeMatchFailure(argumentLocator); } else { // If there are missing arguments, let's add them // using parameter as a template. if (diff < 0) { if (fixMissingArguments(*this, anchor, func1Params, func2Params, abs(diff), loc)) return getTypeMatchFailure(argumentLocator); } else { // If there are extraneous arguments, let's remove // them from the list. if (fixExtraneousArguments(*this, func2, func1Params, diff, loc)) return getTypeMatchFailure(argumentLocator); } } if (diff > 0) { // Drop all of the extraneous arguments. auto numParams = func2Params.size(); func1Params.erase(func1Params.begin() + numParams, func1Params.end()); } } bool hasLabelingFailures = false; for (unsigned i : indices(func1Params)) { auto func1Param = func1Params[i]; auto func2Param = func2Params[i]; // Increase the score if matching an autoclosure parameter to an function // type, so we enforce that non-autoclosure overloads are preferred. // // func autoclosure(f: () -> Int) { } // func autoclosure(f: @autoclosure () -> Int) { } // // let _ = autoclosure as (() -> (Int)) -> () // non-autoclosure preferred // auto isAutoClosureFunctionMatch = [](AnyFunctionType::Param ¶m1, AnyFunctionType::Param ¶m2) { return param1.isAutoClosure() && (!param2.isAutoClosure() && param2.getPlainType()->is()); }; if (isAutoClosureFunctionMatch(func1Param, func2Param) || isAutoClosureFunctionMatch(func2Param, func1Param)) { increaseScore(SK_FunctionToAutoClosureConversion, locator); } // Variadic bit must match. if (func1Param.isVariadic() != func2Param.isVariadic()) { if (!(shouldAttemptFixes() && func2Param.isVariadic())) return getTypeMatchFailure(argumentLocator); auto argType = getFixedTypeRecursive(func1Param.getPlainType(), /*wantRValue=*/true); auto varargsType = func2Param.getPlainType(); // Delay solving this constraint until argument is resolved. if (argType->is()) { addUnsolvedConstraint(Constraint::create( *this, kind, func1, func2, getConstraintLocator(locator))); return getTypeMatchSuccess(); } auto *fix = ExpandArrayIntoVarargs::attempt( *this, argType, varargsType, argumentLocator.withPathElement(LocatorPathElt::ApplyArgToParam( i, i, func2Param.getParameterFlags()))); if (!fix || recordFix(fix)) return getTypeMatchFailure(argumentLocator); continue; } // Labels must match. // // FIXME: We should not end up with labels here at all, but we do // from invalid code in diagnostics, and as a result of code completion // directly building constraint systems. if (func1Param.getLabel() != func2Param.getLabel()) { if (!shouldAttemptFixes()) return getTypeMatchFailure(argumentLocator); // If we are allowed to attempt fixes, let's ignore labeling // failures, and create a fix to re-label arguments if types // line up correctly. hasLabelingFailures = true; } // "isolated" can be added as a subtype relation, but otherwise must match. if (func1Param.isIsolated() != func2Param.isIsolated() && !(func2Param.isIsolated() && subKind >= ConstraintKind::Subtype)) { return getTypeMatchFailure(argumentLocator); } // If functions are differentiable, ensure that @noDerivative is not // discarded. if (func1->isDifferentiable() && func2->isDifferentiable() && func1Param.isNoDerivative() && !func2Param.isNoDerivative()) { return getTypeMatchFailure(argumentLocator); } // Do not allow for functions that expect a sending parameter to match // with a function that expects a non-sending parameter. if (func1Param.getParameterFlags().isSending() && !func2Param.getParameterFlags().isSending()) { auto *fix = AllowSendingMismatch::create( *this, func1, func2, getConstraintLocator(argumentLocator)); if (recordFix(fix)) return getTypeMatchFailure(argumentLocator); } // FIXME: We should check value ownership too, but it's not completely // trivial because of inout-to-pointer conversions. // Compare the parameter types, taking contravariance into account. auto result = matchTypes( func2Param.getOldType(), func1Param.getOldType(), subKind, subflags, (func1Params.size() == 1 ? argumentLocator : argumentLocator.withPathElement( LocatorPathElt::TupleElement(i)))); if (result.isFailure()) return result; } if (hasLabelingFailures && !hasFixFor(loc)) { ConstraintFix *fix = loc->isLastElement() ? AllowArgumentMismatch::create(*this, func1, func2, loc) : ContextualMismatch::create(*this, func1, func2, loc); if (recordFix(fix)) return getTypeMatchFailure(argumentLocator); } } // Result type can be covariant (or equal). return matchTypes(func1->getResult(), func2->getResult(), subKind, subflags, locator.withPathElement(ConstraintLocator::FunctionResult)); } ConstraintSystem::TypeMatchResult ConstraintSystem::matchSuperclassTypes(Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); auto classDecl2 = type2->getClassOrBoundGenericClass(); SmallPtrSet superclasses1; for (auto super1 = type1->getSuperclass(); super1; super1 = super1->getSuperclass()) { auto superclass1 = super1->getClassOrBoundGenericClass(); if (superclass1 != classDecl2) { // Break if we have circular inheritance. if (superclass1 && !superclasses1.insert(superclass1).second) break; continue; } return matchTypes(super1, type2, ConstraintKind::Bind, subflags, locator); } return getTypeMatchFailure(locator); } static ConstraintSystem::TypeMatchResult matchDeepTypeArguments( ConstraintSystem &cs, ConstraintSystem::TypeMatchOptions subflags, ArrayRef args1, ArrayRef args2, ConstraintLocatorBuilder locator, llvm::function_ref recordMismatch = [](unsigned) {}) { if (args1.size() != args2.size()) { return cs.getTypeMatchFailure(locator); } auto allMatch = cs.getTypeMatchSuccess(); for (unsigned i = 0, n = args1.size(); i != n; ++i) { auto result = cs.matchTypes( args1[i], args2[i], ConstraintKind::Bind, subflags, locator.withPathElement(LocatorPathElt::GenericArgument(i))); if (result.isFailure()) { recordMismatch(i); allMatch = result; } } return allMatch; } /// Allow `any Sendable` to match `Any` constraint while matching /// generic arguments i.e. `[any Sendable]` -> `[Any]` when `any Sendable` /// type comes from context that involves `@preconcurrency` declarations /// in non-strict concurrency compiler mode. /// /// Note that it's currently impossible to figure out precisely /// where `any Sendable` type came from. static bool matchSendableExistentialToAnyInGenericArgumentPosition( ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocatorBuilder locator) { // Avoid heavier checks if are not `any Sendable` and `Any`. if (!(lhs->isSendableExistential() || lhs->isAny()) || !(rhs->isSendableExistential() || rhs->isAny())) return false; auto last = locator.last(); // `any Sendable` -> `Any` conversion is allowed for generic arguments // and for function argument/result positions if generic argument is // bound to a function type. if (!last || !(last->is() || last->is() || last->is())) return false; SmallVector path; auto anchor = locator.getLocatorParts(path); { std::optional dropFromIdx; bool inGenericArgumentContext = false; for (unsigned i = 0, n = path.size(); i < n; ++i) { const auto &elt = path[i]; if (elt.is() || elt.is()) { if (!dropFromIdx) dropFromIdx = i; continue; } if (elt.is()) { inGenericArgumentContext = true; continue; } // For example: `[(any Sendable) -> Void]` -> `[(Any) -> Void]` if (elt.is()) { if (inGenericArgumentContext) { // `matchFunctionTypes` accounts for contravariance even under // equality constraint (because it shouldn't matter), but it does // in this case. std::swap(lhs, rhs); } } } // If we are not in generic argument context, // this conversion don't apply. if (!inGenericArgumentContext || !dropFromIdx) return false; // Drop all of the elements that would get in a way of // finding the underlying declaration reference first. path.pop_back_n(path.size() - *dropFromIdx); } if (!(lhs->isSendableExistential() && rhs->isAny())) return false; std::function isPreconcurrencyContext = [&](ConstraintLocator *locator) { if (locator->isLastElement()) return isPreconcurrencyContext( cs.getConstraintLocator(simplifyLocatorToAnchor(locator))); if (locator->directlyAt()) { auto *IOE = castToExpr(locator->getAnchor()); return isPreconcurrencyContext( cs.getConstraintLocator(IOE->getSubExpr())); } auto *calleeLoc = cs.getCalleeLocator(locator); if (!calleeLoc) return false; auto selectedOverload = cs.findSelectedOverloadFor(calleeLoc); if (!(selectedOverload && selectedOverload->choice.isDecl())) return false; if (!selectedOverload->choice.getDecl()->preconcurrency()) { // If the member is not preconcurrency, its base could be. if (auto *UDE = getAsExpr(calleeLoc->getAnchor())) { return isPreconcurrencyContext( cs.getConstraintLocator(UDE->getBase())); } if (auto *SE = getAsExpr(calleeLoc->getAnchor())) { return isPreconcurrencyContext( cs.getConstraintLocator(SE->getBase())); } return false; } return true; }; if (!isPreconcurrencyContext(cs.getConstraintLocator(anchor, path))) return false; // Increase the score to make sure that if there is an overload that // uses `any Sendable` it would be preferred. cs.increaseScore(SK_EmptyExistentialConversion, locator); return true; } ConstraintSystem::TypeMatchResult ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2, ConstraintLocatorBuilder locator) { TypeMatchOptions subflags = TMF_GenerateConstraints; // Handle opaque archetypes. if (auto opaque1 = type1->getAs()) { auto opaque2 = type2->castTo(); assert(opaque1->getDecl() == opaque2->getDecl()); // It's possible to declare a generic requirement like Self == Self.Iterator // where both types are going to be opaque. if (!opaque1->getInterfaceType()->isEqual(opaque2->getInterfaceType())) return getTypeMatchFailure(locator); auto args1 = opaque1->getSubstitutions().getReplacementTypes(); auto args2 = opaque2->getSubstitutions().getReplacementTypes(); if (!shouldAttemptFixes()) { // Match up the replacement types of the respective substitution maps. return matchDeepTypeArguments(*this, subflags, args1, args2, locator); } unsigned numMismatches = 0; auto result = matchDeepTypeArguments(*this, subflags, args1, args2, locator, [&numMismatches](unsigned) { ++numMismatches; }); if (numMismatches > 0) { auto anchor = locator.getAnchor(); // TODO(diagnostics): Only assignments are supported at the moment. if (!isExpr(anchor)) return getTypeMatchFailure(locator); auto *fix = IgnoreAssignmentDestinationType::create( *this, type1, type2, getConstraintLocator(locator)); if (recordFix(fix, /*impact=*/numMismatches)) return getTypeMatchFailure(locator); return getTypeMatchSuccess(); } return result; } // Handle opened archetype types. if (auto opened1 = type1->getAs()) { auto opened2 = type2->castTo(); assert(opened1->getInterfaceType()->isEqual(opened2->getInterfaceType()) && opened1->getGenericEnvironment()->getOpenedExistentialUUID() == opened2->getGenericEnvironment()->getOpenedExistentialUUID()); auto args1 = opened1->getGenericEnvironment() ->getOuterSubstitutions() .getReplacementTypes(); auto args2 = opened2->getGenericEnvironment() ->getOuterSubstitutions() .getReplacementTypes(); return matchDeepTypeArguments(*this, subflags, args1, args2, locator); } // `any Sendable` -> `Any` if (matchSendableExistentialToAnyInGenericArgumentPosition(*this, type1, type2, locator)) return getTypeMatchSuccess(); // Handle existential types. if (auto *existential1 = type1->getAs()) { auto existential2 = type2->castTo(); auto result = matchTypes( existential1->getConstraintType(), existential2->getConstraintType(), ConstraintKind::Bind, subflags, locator.withPathElement(ConstraintLocator::ExistentialConstraintType)); if (result.isFailure()) return result; return getTypeMatchSuccess(); } // Arguments of parameterized protocol types have to match on the nose. if (auto ppt1 = type1->getAs()) { auto ppt2 = type2->castTo(); auto result = matchTypes(ppt1->getBaseType(), ppt2->getBaseType(), ConstraintKind::Bind, subflags, locator.withPathElement( ConstraintLocator::ParentType)); if (result.isFailure()) return result; return matchDeepTypeArguments(*this, subflags, ppt1->getArgs(), ppt2->getArgs(), locator); } // Members of protocol compositions have to match. if (auto pct1 = type1->getAs()) { auto pct2 = type2->castTo(); auto members1 = pct1->getMembers(); auto members2 = pct2->getMembers(); if (members1.size() != members2.size()) return getTypeMatchFailure(locator); if (pct1->getInverses() != pct2->getInverses()) return getTypeMatchFailure(locator); if (pct1->hasExplicitAnyObject() != pct2->hasExplicitAnyObject()) return getTypeMatchFailure(locator); for (unsigned i = 0, e = members1.size(); i < e; ++i) { auto member1 = members1[i]; auto member2 = members2[i]; auto subLocator = locator.withPathElement( LocatorPathElt::ProtocolCompositionMemberType(i)); auto result = matchTypes(member1, member2, ConstraintKind::Bind, subflags, subLocator); if (result.isFailure()) return result; } return getTypeMatchSuccess(); } // Handle nominal types that are not directly generic. if (auto nominal1 = type1->getAs()) { auto nominal2 = type2->castTo(); assert((bool)nominal1->getParent() == (bool)nominal2->getParent() && "Mismatched parents of nominal types"); if (!nominal1->getParent()) return getTypeMatchSuccess(); // Match up the parents, exactly. return matchTypes(nominal1->getParent(), nominal2->getParent(), ConstraintKind::Bind, subflags, locator.withPathElement(ConstraintLocator::ParentType)); } auto bound1 = type1->castTo(); auto bound2 = type2->castTo(); // Match up the parents, exactly, if there are parents. assert((bool)bound1->getParent() == (bool)bound2->getParent() && "Mismatched parents of bound generics"); if (bound1->getParent()) { auto result = matchTypes(bound1->getParent(), bound2->getParent(), ConstraintKind::Bind, subflags, locator.withPathElement( ConstraintLocator::ParentType)); if (result.isFailure()) return result; } auto args1 = bound1->getGenericArgs(); auto args2 = bound2->getGenericArgs(); // Match up the generic arguments, exactly. if (shouldAttemptFixes()) { auto *baseLoc = getConstraintLocator(locator, {LocatorPathElt::GenericType(type1), LocatorPathElt::GenericType(type2)}); // Optionals have a lot of special diagnostics and only one // generic argument so if we're dealing with one, let's allow // `repairFailures` to take care of it. if (bound1->getDecl()->isOptionalDecl()) return matchDeepTypeArguments(*this, subflags, args1, args2, baseLoc); auto argMatchingFlags = subflags; // Allow the solver to produce separate fixes while matching // key path's root/value to a contextual type instead of the // standard one fix for all mismatched generic arguments // because at least one side of such a relation would be resolved. if (!isExpr(locator.trySimplifyToExpr())) { argMatchingFlags |= TMF_ApplyingFix; argMatchingFlags |= TMF_MatchingGenericArguments; } SmallVector mismatches; auto result = matchDeepTypeArguments( *this, argMatchingFlags, args1, args2, baseLoc, [&mismatches](unsigned position) { mismatches.push_back(position); }); if (mismatches.empty()) return result; auto *loc = getConstraintLocator(locator); auto path = loc->getPath(); if (!path.empty()) { // If we have something like ... -> type req # -> pack element #, we're // solving a requirement of the form T : P where T is a type parameter pack if (path.back().is()) path = path.drop_back(); if (path.back().is()) { if (auto *fix = fixRequirementFailure(*this, type1, type2, locator)) { if (recordFix(fix)) return getTypeMatchFailure(locator); increaseScore(SK_Fix, loc, mismatches.size()); return getTypeMatchSuccess(); } } } unsigned impact = 1; if (type1->getAnyPointerElementType() && type2->getAnyPointerElementType()) { // If this is a pointer <-> pointer conversion of different kind, // there is a dedicated restriction/fix for that in some cases. // To accommodate that, let's increase the impact of this fix. impact += 2; } else { // Increase the solution's score for each mismatch this fixes. impact += mismatches.size() - 1; } auto *fix = GenericArgumentsMismatch::create( *this, type1, type2, mismatches, loc); if (!recordFix(fix, impact)) return getTypeMatchSuccess(); return result; } return matchDeepTypeArguments(*this, subflags, args1, args2, locator); } ConstraintSystem::TypeMatchResult ConstraintSystem::matchExistentialTypes(Type type1, Type type2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { // If the first type is a type variable or member thereof, there's nothing // we can do now. if (type1->isTypeVariableOrMember()) { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( Constraint::create(*this, kind, type1, type2, getConstraintLocator(locator))); return getTypeMatchSuccess(); } return getTypeMatchAmbiguous(); } // FIXME: Feels like a hack. if (type1->is()) return getTypeMatchFailure(locator); // FIXME; Feels like a hack...nothing actually "conforms" here, and // we need to disallow conversions from types containing @noescape // functions to Any. // FIXME: special case for nonescaping functions and tuples containing them // shouldn't be needed, as functions have conformances to Escapable/Copyable. if (type2->isAny() && type1->isNoEscape()) { if (shouldAttemptFixes()) { auto *fix = MarkExplicitlyEscaping::create(*this, type1, type2, getConstraintLocator(locator)); if (!recordFix(fix)) return getTypeMatchSuccess(); } return getTypeMatchFailure(locator); } TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); // Handle existential metatypes. if (auto meta1 = type1->getAs()) { ExistentialMetatypeType *meta2; if (auto existential = type2->getAs()) { meta2 = existential->getConstraintType()->getAs(); } else { meta2 = type2->getAs(); } if (meta2) { return matchExistentialTypes(meta1->getInstanceType(), meta2->getInstanceType(), kind, subflags, locator.withPathElement( ConstraintLocator::InstanceType)); } } if (!type2->isExistentialType()) return getTypeMatchFailure(locator); auto layout = type2->getExistentialLayout(); if (auto layoutConstraint = layout.getLayoutConstraint()) { if (layoutConstraint->isClass()) { if (kind == ConstraintKind::ConformsTo || kind == ConstraintKind::NonisolatedConformsTo) { if (!type1->satisfiesClassConstraint()) { if (shouldAttemptFixes()) { if (auto last = locator.last()) { // If solver is in diagnostic mode and type1 is a hole, or if this // is a superclass requirement, let's consider `AnyObject` // conformance solved. The actual superclass requirement // will also fail (because type can't satisfy it), and it's // more interesting for diagnostics. auto req = last->getAs(); if (!req) return getTypeMatchFailure(locator); // Superclass constraints are never satisfied by existentials, // even those that contain the superclass a la `any C & P`. if (!type1->isExistentialType() && (type1->isPlaceholder() || req->getRequirementKind() == RequirementKind::Superclass)) return getTypeMatchSuccess(); auto *fix = fixRequirementFailure(*this, type1, type2, locator); if (fix && !recordFix(fix)) { recordFixedRequirement(getConstraintLocator(locator), type2); return getTypeMatchSuccess(); } } } return getTypeMatchFailure(locator); } } else { // Subtype relation to AnyObject also allows class-bound // existentials that are not @objc and therefore carry // witness tables. if (!type1->isClassExistentialType() && !type1->mayHaveSuperclass()) { if (shouldAttemptFixes()) { llvm::SmallVector path; if (auto anchor = locator.getLocatorParts(path)) { // Let's drop `optional` or `generic argument` bits from // locator because that helps to diagnose reference equality // operators ("===" and "!==") since there is always a // `value-to-optional` or `optional-to-optional` conversion // associated with them (expected argument is `AnyObject?`). if (!path.empty() && (path.back().is() || path.back().is())) path.pop_back(); auto *fixLoc = getConstraintLocator(anchor, path); // If after looking through optionals and generic arguments // we end up directly on assignment this is a source/destination // type mismatch. if (fixLoc->directlyAt()) { auto *fix = IgnoreAssignmentDestinationType::create( *this, type1, type2, fixLoc); return recordFix(fix) ? getTypeMatchFailure(locator) : getTypeMatchSuccess(); } auto *fix = AllowNonClassTypeToConvertToAnyObject::create( *this, type1, fixLoc); return recordFix(fix) ? getTypeMatchFailure(locator) : getTypeMatchSuccess(); } } return getTypeMatchFailure(locator); } } // Keep going. } } if (layout.explicitSuperclass) { auto result = matchTypes(type1, layout.explicitSuperclass, ConstraintKind::Subtype, subflags, locator); if (result.isFailure()) return result; } for (auto *protoDecl : layout.getProtocols()) { switch (simplifyConformsToConstraint(type1, protoDecl, kind, locator, subflags)) { case SolutionKind::Solved: case SolutionKind::Unsolved: break; case SolutionKind::Error: { if (!shouldAttemptFixes()) return getTypeMatchFailure(locator); SmallVector path; auto anchor = locator.getLocatorParts(path); // If the path ends at `optional payload` it means that this // check is part of an implicit value-to-optional conversion, // and it could be safely dropped. if (!path.empty() && path.back().is()) path.pop_back(); // Determine whether this conformance mismatch is // associated with argument to a call, and if so // produce a tailored fix. if (!path.empty()) { auto last = path.back(); if (last.is() || last.is()) { auto proto = protoDecl->getDeclaredInterfaceType(); // Impact is 2 here because there are two failures // 1 - missing conformance and 2 - incorrect argument type. // // This would make sure that arguments with incorrect // conformances are not prioritized over general argument // mismatches. if (type1->isOptional()) { auto unwrappedType = type1->lookThroughAllOptionalTypes(); auto result = simplifyConformsToConstraint( unwrappedType, protoDecl, kind, locator, subflags | TMF_ApplyingFix); if (result == SolutionKind::Solved) { auto fix = ForceOptional::create(*this, type1, proto, getConstraintLocator(locator)); if (recordFix(fix)) return getTypeMatchFailure(locator); break; } } auto fix = AllowArgumentMismatch::create( *this, type1, proto, getConstraintLocator(anchor, path)); if (recordFix(fix, /*impact=*/2)) return getTypeMatchFailure(locator); break; } if ((isExpr(anchor) || isExpr(anchor)) && last.is()) { auto *fix = CollectionElementContextualMismatch::create( *this, type1, type2, getConstraintLocator(anchor, path)); if (recordFix(fix, /*impact=*/2)) return getTypeMatchFailure(locator); break; } // TODO(diagnostics): If there are any requirement failures associated // with result types which are part of a function type conversion, // let's record general conversion mismatch in order for it to capture // and display complete function types. // // Once either reacher locators or better diagnostic presentation for // nested type failures is available this check could be removed. if (last.is()) return getTypeMatchFailure(locator); // If instance types didn't line up correctly, let's produce a // diagnostic which mentions them together with their metatypes. if (last.is()) return getTypeMatchFailure(locator); } else { // There are no elements in the path if (!(isExpr(anchor) || isExpr(anchor))) return getTypeMatchFailure(locator); } if (isExpr(anchor)) { auto *fix = ContextualMismatch::create( *this, type1, type2, getConstraintLocator(anchor, path)); if (recordFix(fix)) return getTypeMatchFailure(locator); break; } auto proto = protoDecl->getDeclaredInterfaceType(); auto *fix = MissingConformance::forContextual( *this, type1, proto, getConstraintLocator(anchor, path)); if (recordFix(fix)) return getTypeMatchFailure(locator); break; } } } // Finally, check parameterized protocol requirements. if (!layout.getParameterizedProtocols().empty()) { SmallVector, 4> fromReqs; if (type1->isExistentialType()) { auto fromLayout = type1->getExistentialLayout(); for (auto *parameterizedType : fromLayout.getParameterizedProtocols()) { auto *protoDecl = parameterizedType->getProtocol(); auto assocTypes = protoDecl->getPrimaryAssociatedTypes(); auto argTypes = parameterizedType->getArgs(); for (unsigned i : indices(argTypes)) { auto argType = argTypes[i]; fromReqs.push_back(std::make_pair(assocTypes[i]->getName(), argType)); } } } for (auto *parameterizedType : layout.getParameterizedProtocols()) { // With two parameterized protocols, we've already made sure conformance // constraints are satisfied. Try to match the arguments! if (type1->isExistentialType()) { auto *protoDecl = parameterizedType->getProtocol(); auto assocTypes = protoDecl->getPrimaryAssociatedTypes(); auto argTypes = parameterizedType->getArgs(); for (unsigned i : indices(argTypes)) { auto argType = argTypes[i]; bool found = false; for (auto fromReq : fromReqs) { if (fromReq.first == assocTypes[i]->getName()) { // FIXME: Extend the locator path to point to the argument // inducing the requirement. auto result = matchTypes(fromReq.second, argType, ConstraintKind::Bind, subflags, locator); if (result.isFailure()) return result; found = true; break; } } if (!found) return getTypeMatchFailure(locator); } } else { // The source type is a concrete type. // // Substitute the source into the requirements of the parameterized type // and discharge the requirements of the parameterized protocol. // // FIXME: Extend the locator path to point to the argument // inducing the requirement. SmallVector reqs; parameterizedType->getRequirements(type1, reqs); for (const auto &req : reqs) { assert(req.getKind() == RequirementKind::SameType); auto result = matchTypes(req.getFirstType(), req.getSecondType(), ConstraintKind::Bind, subflags, locator); if (result.isFailure()) return result; } } } } return getTypeMatchSuccess(); } static bool isStringCompatiblePointerBaseType(ASTContext &ctx, Type baseType) { // Allow strings to be passed to pointer-to-byte or pointer-to-void types. if (baseType->isInt8()) return true; if (baseType->isUInt8()) return true; if (baseType->isVoid()) return true; return false; } /// Determine whether the first type with the given number of optionals /// is potentially more optional than the second type with its number of /// optionals. static bool isPotentiallyMoreOptionalThan(Type type1, Type type2) { SmallVector optionals1; Type objType1 = type1->lookThroughAllOptionalTypes(optionals1); auto numOptionals1 = optionals1.size(); SmallVector optionals2; type2->lookThroughAllOptionalTypes(optionals2); auto numOptionals2 = optionals2.size(); if (numOptionals1 <= numOptionals2 && !objType1->isTypeVariableOrMember()) return false; return true; } /// Enumerate all of the applicable optional conversion restrictions static void enumerateOptionalConversionRestrictions( Type type1, Type type2, ConstraintKind kind, ConstraintLocatorBuilder locator, llvm::function_ref fn) { // Optional-to-optional. if (type1->getOptionalObjectType() && type2->getOptionalObjectType()) fn(ConversionRestrictionKind::OptionalToOptional); // Inject a value into an optional. if (isPotentiallyMoreOptionalThan(type2, type1)) { fn(ConversionRestrictionKind::ValueToOptional); } } /// Determine whether we can bind the given type variable to the given /// fixed type. static bool isBindable(TypeVariableType *typeVar, Type type) { // Disallow recursive bindings. if (ConstraintSystem::typeVarOccursInType(typeVar, type)) return false; // If type variable we are about to bind represents a pack // expansion type, allow the binding to happen regardless of // what the \c type is, because contextual type is just a hint // in this situation and type variable would be bound to its // opened type instead. // // Note that although inference doesn't allow direct bindings to // type variables, they can still get through via `matchTypes` // when type is a partially resolved pack expansion that simplifies // down to a type variable. return typeVar->getImpl().isPackExpansion() || !(type->is() || type->is()); } ConstraintSystem::TypeMatchResult ConstraintSystem::matchTypesBindTypeVar( TypeVariableType *typeVar, Type origType, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator, llvm::function_ref formUnsolvedResult) { assert(typeVar->is() && "Expected a type variable!"); assert(!origType->is() && "Expected a non-type variable!"); // Simplify the right-hand type and perform the "occurs" check. typeVar = getRepresentative(typeVar); auto type = simplifyType(origType, flags); if (!isBindable(typeVar, type)) { if (shouldAttemptFixes()) { // If type variable is allowed to be a hole and it can't be bound to // a particular (full resolved) type, just ignore this binding // instead of re-trying it and failing later. if (typeVar->getImpl().canBindToHole() && !type->hasTypeVariable()) return getTypeMatchSuccess(); // Just like in cases where both sides are dependent member types // with resolved base that can't be simplified to a concrete type // let's ignore this mismatch and mark affected type variable as a hole // because something else has to be fixed already for this to happen. if (type->is() && !type->hasTypeVariable()) { // Since the binding couldn't be performed, the type variable is a // hole regardless whether it would be bound later to some other // type or not. If this is not reflected in constraint system // it would let the solver to form a _valid_ solution as if the // constraint between the type variable and the unresolved dependent // member type never existed. increaseScore(SK_Hole, locator); recordPotentialHole(typeVar); return getTypeMatchSuccess(); } } return formUnsolvedResult(); } // Since member lookup doesn't check requirements // it might sometimes return types which are not // visible in the current context e.g. typealias // defined in constrained extension, substitution // of which might produce error type for base, so // assignment should tread lightly and just fail // if it encounters such types. if (type->hasError()) return getTypeMatchFailure(locator); // Equal constraints allow mixed LValue/RValue bindings, but // if we bind a type to a type variable that can bind to // LValues as part of simplifying the Equal constraint we may // later block a binding of the opposite "LValue-ness" to the // same type variable that happens as part of simplifying // another constraint. if (kind == ConstraintKind::Equal) { if (typeVar->getImpl().canBindToLValue()) return formUnsolvedResult(); type = type->getRValueType(); } // Prevent generic arguments from being assigned `any Sendable` // directly, that should only happen through inference. This is // required because we allow `any Sendable` -> `Any` conversion // in modes without strict concurrency enabled to maintain source // compatibility and let the developers annotate existing APIs // with `any Sendable` and other concurrency attributes. if (typeVar->getImpl().getGenericParameter() && !flags.contains(TMF_BindingTypeVariable) && type->isSendableExistential()) { return formUnsolvedResult(); } // Attempt to fix situations where type variable can't be bound // to a particular type e.g. `l-value` or `inout`. auto fixReferenceMismatch = [&](TypeVariableType *typeVar, Type type) -> bool { if (locator.endsWith()) { auto *fix = IgnoreContextualType::create(*this, typeVar, type, getConstraintLocator(locator)); return !recordFix(fix); } return false; }; // If the left-hand type variable cannot bind to an lvalue, // but we still have an lvalue, fail. if (!typeVar->getImpl().canBindToLValue() && type->hasLValueType()) { if (shouldAttemptFixes() && fixReferenceMismatch(typeVar, type)) return getTypeMatchSuccess(); return getTypeMatchFailure(locator); } // If the left-hand type variable cannot bind to an inout, // but we still have an inout, fail. if (!typeVar->getImpl().canBindToInOut() && type->is()) { if (shouldAttemptFixes() && fixReferenceMismatch(typeVar, type)) return getTypeMatchSuccess(); return getTypeMatchFailure(locator); } // If the left-hand type variable cannot bind to a non-escaping type, // but we still have a non-escaping type, fail. if (!typeVar->getImpl().canBindToNoEscape() && type->isNoEscape()) { if (shouldAttemptFixes()) { auto *fix = MarkExplicitlyEscaping::create(*this, typeVar, type, getConstraintLocator(locator)); if (recordFix(fix)) return getTypeMatchFailure(locator); // Allow no-escape function to be bound with recorded fix. } else { return getTypeMatchFailure(locator); } } if (typeVar->getImpl().isPackExpansion()) { if (!flags.contains(TMF_BindingTypeVariable)) return formUnsolvedResult(); return resolvePackExpansion(typeVar, origType) ? getTypeMatchSuccess() : getTypeMatchFailure(locator); } // If we're attempting to bind a PackType or PackArchetypeType to a type // variable that doesn't support it, we have a pack reference outside of a // pack expansion expression. if (!typeVar->getImpl().canBindToPack() && (type->is() || type->is())) { if (!shouldAttemptFixes()) return getTypeMatchFailure(locator); auto *fix = AllowInvalidPackReference::create( *this, type, getConstraintLocator(locator)); if (recordFix(fix)) return getTypeMatchFailure(locator); // Don't allow the invalid pack reference to propagate to other // bindings. type = PlaceholderType::get(typeVar->getASTContext(), typeVar); } // Binding to a pack expansion type is always an error in Swift 6 mode. // This indicates that a pack expansion expression was used in a context // that doesn't support it. // // In Swift 5 and earlier initializer references are handled in a special // way that uses a type variable to represent a type of the parameter // list. Such type variables should be allowed to bind to a pack expansion // type to support cases where initializer has a single unlabeled variadic // generic parameter - `init(_ data: repeat each T)`. // // See BindTupleOfFunctionParams constraint for more details. if (!typeVar->getImpl().isPackExpansion() && type->is()) { bool representsParameterList = typeVar->getImpl() .getLocator() ->isLastElement(); if (!(typeVar->getImpl().canBindToPack() && representsParameterList) || getASTContext().isSwiftVersionAtLeast(6)) { if (!shouldAttemptFixes()) return getTypeMatchFailure(locator); auto *fix = AllowInvalidPackExpansion::create( *this, getConstraintLocator(locator)); if (recordFix(fix)) return getTypeMatchFailure(locator); // Don't allow the pack expansion type to propagate to other // bindings. type = PlaceholderType::get(typeVar->getASTContext(), typeVar); } } // We do not allow keypaths to go through AnyObject. Let's create a fix // so this can be diagnosed later. if (auto loc = typeVar->getImpl().getLocator()) { auto locPath = loc->getPath(); if (!locPath.empty() && locPath.back().getKind() == ConstraintLocator::KeyPathRoot && type->isAnyObject()) { auto *fix = AllowAnyObjectKeyPathRoot::create( *this, getConstraintLocator(locator)); if (recordFix(fix)) return getTypeMatchFailure(locator); } } // Okay. Bind below. // A constraint that binds any pointer to a void pointer is // ineffective, since any pointer can be converted to a void pointer. if (kind == ConstraintKind::BindToPointerType && type->isVoid()) { // Bind type1 to Void only as a last resort. addConstraint(ConstraintKind::Defaultable, typeVar, type, getConstraintLocator(locator)); return getTypeMatchSuccess(); } // When binding a fixed type to a type variable that cannot contain // lvalues or noescape types, any type variables within the fixed // type cannot contain lvalues or noescape types either. if (type->hasTypeVariable()) { type.visit([&](Type t) { if (auto *tvt = dyn_cast(t.getPointer())) { if (!typeVar->getImpl().canBindToLValue()) { tvt->getImpl().setCanBindToLValue(getTrail(), /*enabled=*/false); } if (!typeVar->getImpl().canBindToNoEscape()) { tvt->getImpl().setCanBindToNoEscape(getTrail(), /*enabled=*/false); } } }); } if (typeVar->getImpl().isClosureType()) { return resolveClosure(typeVar, type, locator) ? getTypeMatchSuccess() : getTypeMatchFailure(locator); } if (typeVar->getImpl().isTapType()) { return resolveTapBody(typeVar, type, locator) ? getTypeMatchSuccess() : getTypeMatchFailure(locator); } if (typeVar->getImpl().isKeyPathType()) { return resolveKeyPath(typeVar, type, flags, locator) ? getTypeMatchSuccess() : getTypeMatchFailure(locator); } assignFixedType(typeVar, type, /*updateState=*/true, /*notifyInference=*/!flags.contains(TMF_BindingTypeVariable)); return getTypeMatchSuccess(); } static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1, Type type2, ASTNode anchor, ArrayRef path) { // Can't fix not yet properly resolved types. if (type1->isTypeVariableOrMember() || type2->isTypeVariableOrMember()) return nullptr; // If we have something like ... -> type req # -> pack element #, we're // solving a requirement of the form T : P where T is a type parameter pack if (path.back().is()) path = path.drop_back(); auto req = path.back().castTo(); if (req.isConditionalRequirement()) { // path is - ... -> open generic -> type req # -> cond req #, // to identify type requirement we only need `open generic -> type req #` // part, because that's how fixes for type requirements are recorded. auto reqPath = path.drop_back(); // If underlying conformance requirement has been fixed, // then there is no reason to fix up conditional requirements. if (cs.hasFixFor(cs.getConstraintLocator(anchor, reqPath))) return nullptr; } auto *reqLoc = cs.getConstraintLocator(anchor, path); switch (req.getRequirementKind()) { case RequirementKind::SameType: return SkipSameTypeRequirement::create(cs, type1, type2, reqLoc); case RequirementKind::SameShape: return SkipSameShapeRequirement::create(cs, type1, type2, reqLoc); case RequirementKind::Superclass: return SkipSuperclassRequirement::create(cs, type1, type2, reqLoc); case RequirementKind::Layout: case RequirementKind::Conformance: return MissingConformance::forRequirement(cs, type1, type2, reqLoc); } llvm_unreachable("covered switch"); } static ConstraintFix *fixPropertyWrapperFailure( ConstraintSystem &cs, Type baseTy, ConstraintLocator *locator, llvm::function_ref attemptFix, std::optional toType = std::nullopt) { // Don't attempt this fix if this is a key path dynamic member // lookup which produced no results. Unwrapping or wrapping // the base type is not going to produce desired results. if (locator->isForKeyPathDynamicMemberLookup()) return nullptr; Expr *baseExpr = nullptr; if (auto *anchor = getAsExpr(locator->getAnchor())) { if (auto *UDE = dyn_cast(anchor)) baseExpr = UDE->getBase(); else if (auto *SE = dyn_cast(anchor)) baseExpr = SE->getBase(); else if (auto *MRE = dyn_cast(anchor)) baseExpr = MRE->getBase(); else if (auto anchor = simplifyLocatorToAnchor(locator)) baseExpr = getAsExpr(anchor); } if (!baseExpr) return nullptr; auto resolvedOverload = cs.findSelectedOverloadFor(baseExpr); if (!resolvedOverload) return nullptr; enum class Fix : uint8_t { ProjectedValue, PropertyWrapper, WrappedValue, }; auto applyFix = [&](Fix fix, VarDecl *decl, Type type) -> ConstraintFix * { if (!decl->hasInterfaceType() || !type) return nullptr; if (baseTy->isEqual(type)) return nullptr; if (baseTy->is() || type->is()) return nullptr; if (!attemptFix(*resolvedOverload, decl, type)) return nullptr; switch (fix) { case Fix::ProjectedValue: case Fix::PropertyWrapper: return UsePropertyWrapper::create(cs, decl, fix == Fix::ProjectedValue, baseTy, toType.value_or(type), locator); case Fix::WrappedValue: return UseWrappedValue::create(cs, decl, baseTy, toType.value_or(type), locator); } llvm_unreachable("Unhandled Fix type in switch"); }; if (auto projection = cs.getPropertyWrapperProjectionInfo(*resolvedOverload)) { if (auto *fix = applyFix(Fix::ProjectedValue, projection->first, projection->second)) return fix; } if (auto wrapper = cs.getPropertyWrapperInformation(*resolvedOverload)) { if (auto *fix = applyFix(Fix::PropertyWrapper, wrapper->first, wrapper->second)) return fix; } if (auto wrappedProperty = cs.getWrappedPropertyInformation(*resolvedOverload)) { if (auto *fix = applyFix(Fix::WrappedValue, wrappedProperty->first, wrappedProperty->second)) return fix; } return nullptr; } static bool canBridgeThroughCast(ConstraintSystem &cs, Type fromType, Type toType) { // If we have a value of type AnyObject that we're trying to convert to // a class, force a downcast. // FIXME: Also allow types bridged through Objective-C classes. if (fromType->isAnyObject() && toType->getClassOrBoundGenericClass()) return true; auto bridged = TypeChecker::getDynamicBridgedThroughObjCClass(cs.DC, fromType, toType); if (!bridged) return false; // Note: don't perform this recovery for NSNumber; if (auto classType = bridged->getAs()) { SmallString<16> scratch; if (classType->getDecl()->isObjC() && classType->getDecl()->getObjCRuntimeName(scratch) == "NSNumber") return false; } return true; } static bool repairViaBridgingCast(ConstraintSystem &cs, Type fromType, Type toType, SmallVectorImpl &conversionsOrFixes, ConstraintLocatorBuilder locator) { // Don't check if any of the types have type variables or placeholders, // `typeCheckCheckedCast` doesn't support checking solver-allocated types. if (fromType->hasTypeVariableOrPlaceholder() || toType->hasTypeVariableOrPlaceholder()) { return false; } auto objectType1 = fromType->getOptionalObjectType(); auto objectType2 = toType->getOptionalObjectType(); if (objectType1 && !objectType2) { auto *anchor = locator.trySimplifyToExpr(); if (!anchor) return false; if (auto overload = cs.findSelectedOverloadFor(anchor)) { auto *decl = overload->choice.getDeclOrNull(); if (decl && decl->isImplicitlyUnwrappedOptional()) fromType = objectType1; } } if (!canBridgeThroughCast(cs, fromType, toType)) return false; if (!TypeChecker::checkedCastMaySucceed(fromType, toType, cs.DC)) return false; conversionsOrFixes.push_back(ForceDowncast::create( cs, fromType, toType, cs.getConstraintLocator(locator))); return true; } /// Return tuple of type and number of optionals on that type. static std::pair getObjectTypeAndNumUnwraps(Type type) { SmallVector optionals; Type objType = type->lookThroughAllOptionalTypes(optionals); return std::make_pair(objType, optionals.size()); } static bool repairViaOptionalUnwrap(ConstraintSystem &cs, Type fromType, Type toType, ConstraintKind matchKind, SmallVectorImpl &conversionsOrFixes, ConstraintLocatorBuilder locator) { fromType = fromType->getWithoutSpecifierType(); if (!fromType->getOptionalObjectType() || toType->is()) return false; // If we have an optional type, try to force-unwrap it. // FIXME: Should we also try '?'? auto *anchor = locator.trySimplifyToExpr(); if (!anchor) return false; // If this is a conversion to a non-optional contextual type e.g. // `let _: Bool = try? foo()` and `foo()` produces `Int` // we should diagnose it as type mismatch instead of missing unwrap. bool possibleContextualMismatch = [&]() { if (!locator.endsWith()) return false; // If the contextual type is optional as well, it's definitely a // missing unwrap. if (toType->getOptionalObjectType()) return false; // If this is a leading-dot syntax member chain with `?.` // notation, it wouldn't be possible to infer the base type // without the contextual type, so we have to treat it as // a missing unwrap. if (auto *OEE = getAsExpr(anchor)) { if (isExpr(OEE->getSubExpr())) return false; } return true; }(); // `OptionalEvaluationExpr` doesn't add a new level of // optionality but it could be hiding concrete types // behind itself which we can use to better understand // how many levels of optionality have to be unwrapped. if (auto *OEE = dyn_cast(anchor)) { auto *subExpr = OEE->getSubExpr(); // First, let's check whether it has been determined that // it was incorrect to use `?` in this position. if (cs.hasFixFor(cs.getConstraintLocator(subExpr), FixKind::RemoveUnwrap)) { if (auto *typeVar = fromType->getOptionalObjectType()->getAs()) { // If the optional chain is invalid let's unwrap optional and // re-introduce the constraint to be solved later once both sides // are sufficiently resolved, this would allow to diagnose not only // the invalid unwrap but an invalid conversion (if any) as well. cs.addConstraint(matchKind, typeVar, toType, cs.getConstraintLocator(locator)); } return true; } auto type = cs.getType(subExpr); // If the type of sub-expression is optional, type of the // `OptionalEvaluationExpr` could be safely ignored because // it doesn't add any type information. if (type->getOptionalObjectType()) fromType = type; // Don't attempt the fix until sub-expression is resolved // if chain is not using leading-dot syntax. This is better // than attempting to propagate type information down optional // chain which is hard to diagnose. if (type->isTypeVariableOrMember() && !isa(subExpr)) return false; // If this is a conversion from optional chain to some // other type e.g. contextual type or a parameter type, // let's use `Bind` to match object types because // object type of the optional chain is a type variable. // // One exception is contextual conversion - in such cases // let's give optional chain a chance to infer its inner type // first, that makes it much easier to diagnose contextual // mismatch vs. missing optional unwrap. if (!possibleContextualMismatch && matchKind >= ConstraintKind::Conversion) matchKind = ConstraintKind::Bind; } if (auto *DRE = dyn_cast(anchor)) { if (DRE->getDecl()->isImplicit()) { // The expression that provides the first type is implicit and never // spelled out in source code, e.g. $match in an expression pattern. // Thus we cannot force unwrap the first type return false; } } if (auto *optTryExpr = dyn_cast(anchor)) { auto subExprType = cs.getType(optTryExpr->getSubExpr()); const bool isSwift5OrGreater = cs.getASTContext().LangOpts.isSwiftVersionAtLeast(5); if (subExprType->getOptionalObjectType()) { if (isSwift5OrGreater) { // For 'try?' expressions, a ForceOptional fix converts 'try?' // to 'try!'. If the sub-expression is optional, then a force-unwrap // won't change anything in Swift 5+ because 'try?' already avoids // adding an additional layer of Optional there. return false; } } else { // In cases when sub-expression isn't optional, 'try?' // always adds one level of optionality regardless of // language mode, so we can safely try to bind its // object type to contextual type without risk of // causing more optionality mismatches down the road. // // For contextual conversions let's give `try?` a chance to // infer inner type which, if incorrect, should result in // contextual conversion failure instead of optional unwrap. matchKind = possibleContextualMismatch ? ConstraintKind::Conversion : ConstraintKind::Bind; } } Type fromObjectType, toObjectType; unsigned fromUnwraps, toUnwraps; std::tie(fromObjectType, fromUnwraps) = getObjectTypeAndNumUnwraps(fromType); std::tie(toObjectType, toUnwraps) = getObjectTypeAndNumUnwraps(toType); // Since equality is symmetric and it decays into a `Bind`, eagerly // unwrapping optionals from either side might be incorrect since // there is not enough information about what is expected e.g. // `Int?? equal T0?` just like `T0? equal Int??` allows `T0` to be // bound to `Int?` and there is no need to unwrap. Solver has to wait // until more information becomes available about what `T0` is expected // to be before taking action. if (matchKind == ConstraintKind::Equal && (fromObjectType->is() || toObjectType->is())) { return false; } // If `from` is not less optional than `to`, force unwrap is // not going to help here. In case of object type of `from` // is a type variable, let's assume that it might be optional. if (fromUnwraps <= toUnwraps && !fromObjectType->is()) return false; // If the result of optional chaining is converted to // an optional contextual type represented by a type // variable e.g. `T?`, there can be no optional mismatch // because `T` could be bound to an optional of any depth. if (isa(anchor) && toUnwraps > 0) { if (locator.endsWith() && toObjectType->is()) return false; } auto result = cs.matchTypes(fromObjectType, toObjectType, matchKind, ConstraintSystem::TypeMatchFlags::TMF_ApplyingFix, locator); if (!result.isSuccess()) return false; conversionsOrFixes.push_back(ForceOptional::create( cs, fromType, toType, cs.getConstraintLocator(locator))); return true; } static bool repairArrayLiteralUsedAsDictionary( ConstraintSystem &cs, Type arrayType, Type dictType, ConstraintKind matchKind, SmallVectorImpl &conversionsOrFixes, ConstraintLocator *loc) { if (auto *fix = TreatArrayLiteralAsDictionary::attempt(cs, dictType, arrayType, loc)) { // Ignore any attempts at promoting the value to an optional as even after // stripping off all optionals above the underlying types won't match (array // vs dictionary). conversionsOrFixes.erase( llvm::remove_if(conversionsOrFixes, [&](RestrictionOrFix &E) { if (auto restriction = E.getRestriction()) return *restriction == ConversionRestrictionKind:: ValueToOptional || *restriction == ConversionRestrictionKind:: OptionalToOptional; return false; }), conversionsOrFixes.end()); conversionsOrFixes.push_back(fix); return true; } return false; } /// Let's check whether this is an out-of-order argument in binary /// operator/function with concrete type parameters e.g. /// `func ^^(x: Int, y: String)` called as `"" ^^ 42` instead of /// `42 ^^ ""` and repair it by using out-of-order fix on the /// parent locator. static bool repairOutOfOrderArgumentsInBinaryFunction( ConstraintSystem &cs, SmallVectorImpl &conversionsOrFixes, ConstraintLocator *locator) { if (!locator->isLastElement()) return false; auto path = locator->getPath(); auto *parentLoc = cs.getConstraintLocator(locator->getAnchor(), path.drop_back()); if (cs.hasFixFor(parentLoc, FixKind::MoveOutOfOrderArgument)) return true; auto *calleeLoc = cs.getCalleeLocator(locator); if (!calleeLoc) return false; auto overload = cs.findSelectedOverloadFor(calleeLoc); if (!(overload && overload->choice.isDecl())) return false; auto *fnType = overload->adjustedOpenedType->getAs(); if (!(fnType && fnType->getNumParams() == 2)) return false; auto argument = simplifyLocatorToAnchor(locator); // Argument could be synthesized. if (!argument) return false; auto argLoc = locator->castLastElementTo(); auto currArgIdx = argLoc.getArgIdx(); auto currParamIdx = argLoc.getParamIdx(); // Argument is extraneous and has been re-ordered to match one // of two parameter types. if (currArgIdx >= 2 || currArgIdx != currParamIdx) return false; auto otherArgIdx = currArgIdx == 0 ? 1 : 0; auto argType = cs.getType(argument); auto paramType = fnType->getParams()[otherArgIdx].getOldType(); bool isOperatorRef = overload->choice.getDecl()->isOperator(); // If one of the parameters is `inout`, we can't flip the arguments. { auto params = fnType->getParams(); if (params[0].isInOut() != params[1].isInOut()) return false; } auto getReorderedArgumentLocator = [&](unsigned argIdx) { auto paramIdx = argIdx == 0 ? 1 : 0; return cs.getConstraintLocator( parentLoc, LocatorPathElt::ApplyArgToParam( argIdx, paramIdx, fnType->getParams()[paramIdx].getParameterFlags())); }; auto matchArgToParam = [&](Type argType, Type paramType, unsigned argIdx) { auto *loc = getReorderedArgumentLocator(argIdx); // If argument (and/or parameter) is a generic type let's not even try this // fix because it would be impossible to match given types without delaying // until more context becomes available. if (argType->hasTypeVariable() || paramType->hasTypeVariable()) return cs.getTypeMatchFailure(loc); // FIXME: There is currently no easy way to avoid attempting // fixes, matchTypes do not propagate `TMF_ApplyingFix` flag. llvm::SaveAndRestore options( cs.Options, cs.Options - ConstraintSystemFlags::AllowFixes); // Check optionality, if argument is more optional than parameter // they are not going to match. This saves us one disjunction because // optionals are matched as deep-equality and optional-to-optional. { unsigned numArgUnwraps; unsigned numParamUnwraps; std::tie(argType, numArgUnwraps) = getObjectTypeAndNumUnwraps(argType); std::tie(paramType, numParamUnwraps) = getObjectTypeAndNumUnwraps(paramType); if (numArgUnwraps > numParamUnwraps) return cs.getTypeMatchFailure(loc); } return cs.matchTypes( argType, paramType, isOperatorRef ? ConstraintKind::OperatorArgumentConversion : ConstraintKind::ArgumentConversion, ConstraintSystem::TypeMatchFlags::TMF_ApplyingFix, loc); }; auto result = matchArgToParam(argType, paramType, currArgIdx); if (result.isSuccess()) { // Let's check whether other argument matches current parameter type, // if it does - it's definitely out-of-order arguments issue. auto *otherArgLoc = getReorderedArgumentLocator(otherArgIdx); auto otherArg = simplifyLocatorToAnchor(otherArgLoc); // Argument could be synthesized. if (!otherArg) return false; argType = cs.getType(otherArg); paramType = fnType->getParams()[currArgIdx].getOldType(); result = matchArgToParam(argType, paramType, otherArgIdx); if (result.isSuccess()) { conversionsOrFixes.push_back(MoveOutOfOrderArgument::create( cs, otherArgIdx, currArgIdx, {{0}, {1}}, parentLoc)); return true; } } return false; } /// Attempt to repair typing failures and record fixes if needed. /// \return true if at least some of the failures has been repaired /// successfully, which allows type matcher to continue. bool ConstraintSystem::repairFailures( Type lhs, Type rhs, ConstraintKind matchKind, TypeMatchOptions flags, SmallVectorImpl &conversionsOrFixes, ConstraintLocatorBuilder locator) { SmallVector path; auto anchor = locator.getLocatorParts(path); // If there is a missing explicit call it could be: // // a). Contextual e.g. `let _: R = foo` // b). Argument is a function value passed to parameter // which expects its result type e.g. `foo(bar)` // c). Assignment destination type matches return type of // of the function value e.g. `foo = bar` or `foo = .bar` auto repairByInsertingExplicitCall = [&](Type srcType, Type dstType) -> bool { auto fnType = srcType->getAs(); if (!fnType) return false; // If the locator isn't anchored at an expression, or the expression is // implicit, don't try to insert an explicit call into the source code. auto *loc = getConstraintLocator(locator); auto *anchor = getAsExpr(simplifyLocatorToAnchor(loc)); if (!anchor || anchor->isImplicit()) return false; if (isArgumentOfPatternMatchingOperator(loc)) return false; // Don't attempt this fix for trailing closures. if (auto elt = loc->getLastElementAs()) { auto argumentList = getArgumentList(loc); if (argumentList->isTrailingClosureIndex(elt->getArgIdx())) return false; } // If argument is a function type and all of its parameters have // default values, let's see whether error is related to missing // explicit call. if (fnType->getNumParams() > 0) { auto overload = findSelectedOverloadFor(anchor); if (!(overload && overload->choice.isDecl())) return false; const auto &choice = overload->choice; ParameterListInfo info(fnType->getParams(), choice.getDecl(), hasAppliedSelf(*this, choice)); if (llvm::any_of(indices(fnType->getParams()), [&info](const unsigned idx) { return !info.hasDefaultArgument(idx); })) return false; } auto resultType = fnType->getResult(); // If this is situation like `x = { ... }` where closure results in // `Void`, let's not suggest to call the closure, because it's most // likely not intended. if (auto *assignment = getAsExpr(anchor)) { if (isa(assignment->getSrc()) && resultType->isVoid()) return false; } // If left-hand side is a function type but right-hand // side isn't, let's check it would be possible to fix // this by forming an explicit call. auto convertTo = dstType->lookThroughAllOptionalTypes(); // If the RHS is a function type, the source must be a function-returning // function. if (convertTo->is() && !resultType->is()) return false; // Right-hand side can't be a type variable or dependent member, or `Any` // (if function conversion to `Any` didn't succeed there is something else // going on e.g. problem with escapiness). if (convertTo->isTypeVariableOrMember() || convertTo->isAny()) return false; ConstraintKind matchKind; if (resultType->is()) { matchKind = ConstraintKind::Equal; } else { matchKind = ConstraintKind::Conversion; } auto result = matchTypes(resultType, dstType, matchKind, TypeMatchFlags::TMF_ApplyingFix, locator); if (result.isSuccess()) { conversionsOrFixes.push_back( InsertExplicitCall::create(*this, getConstraintLocator(locator))); return true; } return false; }; auto repairByAnyToAnyObjectCast = [&](Type lhs, Type rhs) -> bool { if (!(lhs->isAny() && rhs->isAnyObject())) return false; conversionsOrFixes.push_back(MissingConformance::forContextual( *this, lhs, rhs, getConstraintLocator(locator))); return true; }; auto repairByTreatingRValueAsLValue = [&](Type lhs, Type rhs) -> bool { if (!lhs->is() && (rhs->is() || rhs->is())) { // Conversion from l-value to inout in an operator argument // position (which doesn't require explicit `&`) decays into // a `Bind` of involved object types, same goes for explicit // `&` conversion from l-value to inout type. // // In case of regular argument conversion although explicit `&` // is required we still want to diagnose the problem as one // about mutability instead of suggesting to add `&` which wouldn't // be correct. auto kind = (isExpr(anchor) || (rhs->is() && (matchKind == ConstraintKind::ArgumentConversion || matchKind == ConstraintKind::OperatorArgumentConversion))) ? ConstraintKind::Bind : matchKind; auto result = matchTypes(lhs, rhs->getWithoutSpecifierType(), kind, TMF_ApplyingFix, locator); if (result.isSuccess()) { // If left side is a hole, let's not record a fix since hole can // assume any type and already represents a problem elsewhere in // the expression. if (lhs->isPlaceholder()) return true; auto *loc = getConstraintLocator(locator); // If this `inout` is in incorrect position, it should be diagnosed // by other fixes. if (loc->directlyAt()) { if (!getArgumentLocator(castToExpr(anchor))) { conversionsOrFixes.push_back( RemoveAddressOf::create(*this, lhs, rhs, loc)); return true; } } conversionsOrFixes.push_back(TreatRValueAsLValue::create(*this, loc)); return true; } } return false; }; // Check whether given `value` type matches a `RawValue` type of // a given raw representable type. auto isValueOfRawRepresentable = [&](Type valueType, Type rawReprType) -> bool { // diagnostic is going to suggest failable initializer anyway. if (auto objType = rawReprType->getOptionalObjectType()) rawReprType = objType; // If value is optional diagnostic would suggest using `Optional.map` in // combination with `(rawValue: ...)` initializer. if (auto objType = valueType->getOptionalObjectType()) valueType = objType; if (rawReprType->isTypeVariableOrMember() || rawReprType->isPlaceholder()) return false; auto rawValue = isRawRepresentable(*this, rawReprType); if (!rawValue) return false; auto result = matchTypes(valueType, rawValue, ConstraintKind::Conversion, TMF_ApplyingFix, locator); return !result.isFailure(); }; // Check whether given `rawReprType` does indeed conform to `RawRepresentable` // and if so check that given `expectedType` matches its `RawValue` type. If // that condition holds add a tailored fix which is going to suggest to // explicitly construct a raw representable type from a given value type. auto repairByConstructingRawRepresentableType = [&](Type expectedType, Type rawReprType) -> bool { if (!isValueOfRawRepresentable(expectedType, rawReprType)) return false; conversionsOrFixes.push_back(ExplicitlyConstructRawRepresentable::create( *this, rawReprType, expectedType, getConstraintLocator(locator))); return true; }; // Check whether given `rawReprType` does indeed conform to `RawRepresentable` // and if so check that given `expectedType` matches its `RawValue` type. If // that condition holds add a tailored fix which is going to suggest to // use `.rawValue` associated with given raw representable type to match // given expected type. auto repairByUsingRawValueOfRawRepresentableType = [&](Type rawReprType, Type expectedType) -> bool { if (!isValueOfRawRepresentable(expectedType, rawReprType)) return false; conversionsOrFixes.push_back(UseRawValue::create( *this, rawReprType, expectedType, getConstraintLocator(locator))); return true; }; auto hasConversionOrRestriction = [&](ConversionRestrictionKind kind) { return llvm::any_of(conversionsOrFixes, [kind](const RestrictionOrFix correction) { if (auto restriction = correction.getRestriction()) return restriction == kind; return false; }); }; auto hasAnyRestriction = [&]() { return llvm::any_of(conversionsOrFixes, [](const RestrictionOrFix &correction) { return bool(correction.getRestriction()); }); }; // Check whether this is a tuple with a single unlabeled element // i.e. `(_: Int)` and return type of that element if so. Note that // if the element is pack expansion type the tuple is significant. auto isSingleUnlabeledElementTuple = [](Type type) -> Type { if (auto *tuple = type->getAs()) { if (tuple->getNumElements() == 1 && !tuple->getElement(0).hasName()) { auto eltType = tuple->getElement(0).getType(); return isPackExpansionType(eltType) ? Type() : eltType; } } return Type(); }; if (repairArrayLiteralUsedAsDictionary(*this, lhs, rhs, matchKind, conversionsOrFixes, getConstraintLocator(locator))) return true; if (locator.endsWith()) { conversionsOrFixes.push_back( IgnoreThrownErrorMismatch::create(*this, lhs, rhs, getConstraintLocator(locator))); return true; } auto maybeRepairKeyPathResultFailure = [&](KeyPathExpr *kpExpr) { if (lhs->isPlaceholder() || rhs->isPlaceholder()) return true; if (lhs->isTypeVariableOrMember() || rhs->isTypeVariableOrMember()) return false; if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) || hasConversionOrRestriction(ConversionRestrictionKind::ValueToOptional)) return false; auto i = kpExpr->getComponents().size() - 1; auto lastCompLoc = getConstraintLocator(kpExpr, LocatorPathElt::KeyPathComponent(i)); if (hasFixFor(lastCompLoc, FixKind::AllowTypeOrInstanceMember)) return true; auto *keyPathLoc = getConstraintLocator(anchor); if (hasFixFor(keyPathLoc)) return true; if (auto contextualInfo = getContextualTypeInfo(anchor)) { if (hasFixFor(getConstraintLocator( keyPathLoc, LocatorPathElt::ContextualType(contextualInfo->purpose)))) return true; } conversionsOrFixes.push_back(IgnoreContextualType::create( *this, lhs, rhs, getConstraintLocator(keyPathLoc, ConstraintLocator::KeyPathValue))); return true; }; if (path.empty()) { if (!anchor) return false; // This could be: // - `InOutExpr` used with r-value e.g. `foo(&x)` where `x` is a `let`. // - `ForceValueExpr` e.g. `foo.bar! = 42` where `bar` or `foo` are // immutable or a subscript e.g. `foo["bar"]! = 42`. if (repairByTreatingRValueAsLValue(lhs, rhs)) return true; // If method reference forms a value type of the key path, // there is going to be a constraint to match result of the // member lookup to the generic parameter `V` of *KeyPath // type associated with key path expression, which we need to // fix-up here unless last component has already a invalid type or // instance fix recorded. if (isExpr(anchor)) { if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) { // If we have a conversion happening here, we should let fix happen in // simplifyRestrictedConstraint. if (hasAnyRestriction()) return false; } conversionsOrFixes.push_back(IgnoreContextualType::create( *this, lhs, rhs, getConstraintLocator(locator))); return true; } if (isExpr(anchor)) { if (lhs->is()) { conversionsOrFixes.push_back( TreatRValueAsLValue::create(*this, getConstraintLocator(locator))); return true; } } if (auto *OEE = getAsExpr(anchor)) { // If concrete type of the sub-expression can't be converted to the // type associated with optional evaluation result it could only be // contextual mismatch where type of the top-level expression // comes from contextual type or its parent expression. // // Because result type of the optional evaluation is supposed to // represent the type of its sub-expression with added level of // optionality if needed. auto contextualTy = simplifyType(rhs)->getOptionalObjectType(); if (!lhs->getOptionalObjectType() && !lhs->hasTypeVariable() && contextualTy && !contextualTy->isTypeVariableOrMember()) { auto *fixLocator = getConstraintLocator(OEE->getSubExpr()); // If inner expression already has a fix, consider this two-way // mismatch as un-salvageable. if (hasFixFor(fixLocator)) return false; conversionsOrFixes.push_back( IgnoreContextualType::create(*this, lhs, rhs, fixLocator)); return true; } } if (auto *AE = getAsExpr(anchor)) { if (repairByInsertingExplicitCall(lhs, rhs)) return true; if (auto *inoutExpr = dyn_cast(AE->getSrc())) { auto *loc = getConstraintLocator(inoutExpr); // Remove all of the restrictions because none of them // are going to succeed. conversionsOrFixes.erase( llvm::remove_if( conversionsOrFixes, [](const auto &entry) { return bool(entry.getRestriction()); }), conversionsOrFixes.end()); if (hasFixFor(loc, FixKind::RemoveAddressOf)) return true; conversionsOrFixes.push_back( RemoveAddressOf::create(*this, lhs, rhs, loc)); return true; } if (repairByAnyToAnyObjectCast(lhs, rhs)) return true; if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator)) return true; if (hasAnyRestriction()) return false; // If destination is `AnyObject` it means that source doesn't conform. if (rhs->getWithoutSpecifierType() ->lookThroughAllOptionalTypes() ->isAnyObject()) { conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create( *this, lhs, rhs, getConstraintLocator(locator))); return true; } auto *destExpr = AE->getDest(); // Literal expression as well as call/operator application can't be // used as an assignment destination because resulting type is immutable. if (isa(destExpr) || isa(destExpr)) { conversionsOrFixes.push_back( TreatRValueAsLValue::create(*this, getConstraintLocator(locator))); return true; } // If destination has a function type, it might either be // a property with a function type or a method reference, // e.g. `foo.bar = 42` neither can be used if the destination // is not l-value. auto destType = getType(destExpr); auto destTypeVar = destType->getAs(); bool destIsOrCanBindToLValue = destType->is() || (destTypeVar && destTypeVar->getImpl().canBindToLValue()); if (!destIsOrCanBindToLValue && rhs->is()) { conversionsOrFixes.push_back( TreatRValueAsLValue::create(*this, getConstraintLocator(locator))); return true; } if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) return true; // `rhs` - is an assignment destination and `lhs` is its source. if (repairByConstructingRawRepresentableType(lhs, rhs)) return true; if (repairByUsingRawValueOfRawRepresentableType(lhs, rhs)) return true; // If either side is a placeholder then let's consider this // assignment correctly typed. if (lhs->isPlaceholder() || rhs->isPlaceholder()) return true; // Let's try to match source and destination types one more // time to see whether they line up, if they do - the problem is // related to immutability, otherwise it's a type mismatch. auto result = matchTypes(lhs, rhs, ConstraintKind::Conversion, TMF_ApplyingFix, locator); auto *loc = getConstraintLocator(locator); if (destIsOrCanBindToLValue || result.isFailure()) { // Let this assignment failure be diagnosed by the // AllowTupleTypeMismatch fix already recorded. if (hasFixFor(loc, FixKind::AllowTupleTypeMismatch)) return true; conversionsOrFixes.push_back( IgnoreAssignmentDestinationType::create(*this, lhs, rhs, loc)); } else { conversionsOrFixes.push_back(TreatRValueAsLValue::create(*this, loc)); } return true; } return false; } if (auto *VD = getAsDecl(anchor)) { // Matching a witness to an protocol requirement. if (auto witnessElt = path[0].getAs()) { if (isa(VD->getDeclContext()) && VD->isProtocolRequirement()) { auto *witness = witnessElt->getDecl(); if ((VD->preconcurrency() || witness->preconcurrency()) && // Note that the condition below is very important, // we need to wait until the very last moment to strip // the concurrency annotations from the innermost type. conversionsOrFixes.empty()) { // Allow requirements/witnesses to introduce `swift_attr` and other // concurrency related annotations (e.g. `& Sendable` or `@Sendable`) // (note that `swift_attr` in type contexts weren't supported // before) and for witnesses to adopt them gradually by matching // with a warning in non-strict concurrency mode. if (!(Context.isSwiftVersionAtLeast(6) || Context.LangOpts.StrictConcurrencyLevel == StrictConcurrency::Complete)) { auto strippedLHS = lhs->stripConcurrency(/*recursive=*/true, /*dropGlobalActor=*/true); auto strippedRHS = rhs->stripConcurrency(/*recursive=*/true, /*dropGlobalActor=*/true); // If nothing got stripped there is no reason to re-match // the types. if (!strippedLHS->isEqual(lhs) || !strippedRHS->isEqual(rhs)) { auto result = matchTypes(strippedLHS, strippedRHS, matchKind, flags | TMF_ApplyingFix, locator); if (!result.isFailure()) { increaseScore(SK_MissingSynthesizableConformance, locator); return true; } } } } } } } // If there is a conversion associated with an existential member access // along the path, the problem is that the constraint system does not support // the (formally sane) upcast required to access the member. if (llvm::find_if(path, [](const LocatorPathElt &elt) -> bool { return elt.is(); }) != path.end()) { if (auto overload = findSelectedOverloadFor(castToExpr(anchor))) { auto &choice = overload->choice; conversionsOrFixes.push_back(AllowMemberRefOnExistential::create( *this, choice.getBaseType(), choice.getDecl(), DeclNameRef(choice.getDecl()->getName()), getConstraintLocator(locator))); return true; } } auto elt = path.back(); switch (elt.getKind()) { case ConstraintLocator::LValueConversion: { // Ignore l-value conversion element since it has already // played its role. path.pop_back(); // If this is a contextual mismatch between l-value types e.g. // `@lvalue String vs. @lvalue Int`, let's pretend that it's okay. if (!path.empty()) { if (path.back().is()) { auto *locator = getConstraintLocator(anchor, path.back()); conversionsOrFixes.push_back( IgnoreContextualType::create(*this, lhs, rhs, locator)); break; } // There is no subtyping between object types of inout argument/parameter. if (auto argConv = path.back().getAs()) { // Attempt conversions first. if (hasAnyRestriction()) break; // Unwraps are allowed to preserve l-valueness so we can suggest // them here. if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) return true; auto *loc = getConstraintLocator(locator); auto result = matchTypes(lhs, rhs, ConstraintKind::Conversion, TMF_ApplyingFix, locator); ConstraintFix *fix = nullptr; if (result.isFailure()) { // If this is a "destination" argument to a mutating operator // like `+=`, let's consider it contextual and only attempt // to fix type mismatch on the "source" right-hand side of // such operators. if (isOperatorArgument(loc) && argConv->getArgIdx() == 0) break; fix = AllowArgumentMismatch::create(*this, lhs, rhs, loc); } else { fix = AllowInOutConversion::create(*this, lhs, rhs, loc); } conversionsOrFixes.push_back(fix); break; } // If this is a problem with result type of a subscript setter, // let's re-attempt to repair without l-value conversion in the // locator to fix underlying type mismatch. if (path.back().is()) { return repairFailures(lhs, rhs, matchKind, flags, conversionsOrFixes, getConstraintLocator(anchor, path)); } // If this is a function type param type mismatch in any position, // the mismatch we want to report is for the whole structural type. auto last = std::find_if( path.rbegin(), path.rend(), [](LocatorPathElt &elt) -> bool { return elt.is(); }); if (last != path.rend()) break; } break; } case ConstraintLocator::ApplyArgToParam: { auto loc = getConstraintLocator(locator); // If this type mismatch is associated with a synthesized argument, // let's just ignore it because the main problem is the absence of // the argument. if (auto applyLoc = elt.getAs()) { if (auto *argumentList = getArgumentList(loc)) { // This is either synthesized argument or a default value. if (applyLoc->getArgIdx() >= argumentList->size()) { auto *calleeLoc = getCalleeLocator(loc); auto overload = findSelectedOverloadFor(calleeLoc); // If this cannot be a default value matching, let's ignore. if (!(overload && overload->choice.isDecl())) return true; // Ignore decls that don't have meaningful parameter lists - this // matches variables and parameters with function types. auto *paramList = overload->choice.getDecl()->getParameterList(); if (!paramList) return true; if (!paramList->get(applyLoc->getParamIdx())->getTypeOfDefaultExpr()) return true; } } } // Don't attempt to fix an argument being passed to a // _OptionalNilComparisonType parameter. Such an overload should only take // effect when a nil literal is used in valid code, and doesn't offer any // useful fixes for invalid code. if (auto *nominal = rhs->getAnyNominal()) { if (nominal->isStdlibDecl() && nominal->getName() == getASTContext().Id_OptionalNilComparisonType) { return false; } } if (isForCodeCompletion()) { // If the argument contains the code completion location, the user has not // finished typing out this argument yet. Treat the mismatch as valid so // we don't penalize this solution. if (auto *arg = getAsExpr(simplifyLocatorToAnchor(loc))) { // Ignore synthesized args like $match in implicit pattern match // operator calls. Their source location is usually the same as the // other (explicit) argument's so source range containment alone isn't // sufficient. bool isSynthesizedArg = arg->isImplicit() && isa(arg); if (!isSynthesizedArg && isForCodeCompletion() && containsIDEInspectionTarget(arg) && !lhs->isVoid() && !lhs->isUninhabited()) return true; } } if (repairByInsertingExplicitCall(lhs, rhs)) break; bool isPatternMatching = isArgumentOfPatternMatchingOperator(loc); // Let's not suggest force downcasts in pattern-matching context. if (!isPatternMatching && repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator)) break; // Argument is a r-value but parameter expects an l-value e.g. // // func foo(_ x: inout Int) {} // let x: Int = 42 // foo(x) // `x` can't be converted to `inout Int`. // // This has to happen before checking for optionality mismatch // because otherwise `Int? arg conv inout Int` is going to get // fixed as 2 fixes - "force unwrap" + r-value -> l-value mismatch. if (repairByTreatingRValueAsLValue(lhs, rhs)) break; // If argument in l-value type and parameter is `inout` or a pointer, // let's see if it's generic parameter matches and suggest adding explicit // `&`. if (lhs->is() && (rhs->is() || rhs->getAnyPointerElementType())) { auto baseType = rhs->is() ? rhs->getInOutObjectType() : rhs->getAnyPointerElementType(); // Let's use `BindToPointer` constraint here to match up base types // of implied `inout` argument and `inout` or pointer parameter. // This helps us to avoid implicit conversions associated with // `ArgumentConversion` constraint. auto result = matchTypes(lhs->getRValueType(), baseType, ConstraintKind::BindToPointerType, TypeMatchFlags::TMF_ApplyingFix, locator); if (result.isSuccess()) { conversionsOrFixes.push_back(AddAddressOf::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } } // If the argument is inout and the parameter is not inout or a pointer, // suggest removing the &. if (lhs->is() && !rhs->is()) { auto objectType = rhs->lookThroughAllOptionalTypes(); if (!objectType->getAnyPointerElementType()) { auto result = matchTypes(lhs->getInOutObjectType(), rhs, ConstraintKind::ArgumentConversion, TypeMatchFlags::TMF_ApplyingFix, locator); if (result.isSuccess()) { conversionsOrFixes.push_back(RemoveAddressOf::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } } } // If parameter type is `Any` the problem might be related to // invalid escapiness of the argument. if (rhs->isAny()) break; // If there are any restrictions here we need to wait and let // `simplifyRestrictedConstraintImpl` handle them. if (hasAnyRestriction()) break; if (auto *fix = fixPropertyWrapperFailure( *this, lhs, loc, [&](SelectedOverload overload, VarDecl *decl, Type newBase) { // FIXME: There is currently no easy way to avoid attempting // fixes, matchTypes do not propagate `TMF_ApplyingFix` flag. llvm::SaveAndRestore options( Options, Options - ConstraintSystemFlags::AllowFixes); TypeMatchOptions flags; return matchTypes(newBase, rhs, ConstraintKind::Subtype, flags, getConstraintLocator(locator)) .isSuccess(); }, rhs)) { conversionsOrFixes.push_back(fix); break; } // If this is an implicit 'something-to-pointer' conversion // it's going to be diagnosed by specialized fix which deals // with generic argument mismatches. if (matchKind == ConstraintKind::BindToPointerType) { if (!rhs->isPlaceholder()) break; } // If this is a ~= operator implicitly generated by pattern matching // let's not try to fix right-hand side of the operator because it's // a correct contextual type. if (isPatternMatching && elt.castTo().getParamIdx() == 1) break; if (auto *fix = ExpandArrayIntoVarargs::attempt(*this, lhs, rhs, locator)) { conversionsOrFixes.push_back(fix); break; } // If parameter is a collection but argument is not, let's try // to try and match collection element type to the argument to // produce better diagnostics e.g.: // // ``` // func foo(_: [T]) {} // foo(1) // expected '[Int]', got 'Int' // ``` if (rhs->isKnownStdlibCollectionType()) { std::function getArrayOrSetType = [&](Type type) -> Type { if (auto eltTy = type->getArrayElementType()) return getArrayOrSetType(eltTy); if (auto eltTy = isSetType(type)) return getArrayOrSetType(*eltTy); return type; }; // Let's ignore any optional types associated with element e.g. `[T?]` auto rhsEltTy = getArrayOrSetType(rhs)->lookThroughAllOptionalTypes(); (void)matchTypes(lhs, rhsEltTy, ConstraintKind::Equal, TMF_ApplyingFix, locator); } // If either type has a placeholder, consider this fixed. if (lhs->hasPlaceholder() || rhs->hasPlaceholder()) return true; // `lhs` - is an argument and `rhs` is a parameter type. if (repairByConstructingRawRepresentableType(lhs, rhs)) break; if (repairByUsingRawValueOfRawRepresentableType(lhs, rhs)) break; if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) return true; { auto *calleeLocator = getCalleeLocator(loc); if (hasFixFor(calleeLocator, FixKind::AddQualifierToAccessTopLevelName)) { if (auto overload = findSelectedOverloadFor(calleeLocator)) { if (auto choice = overload->choice.getDeclOrNull()) { // If this is an argument of a symmetric function/operator let's // not fix any position rather than first because we'd just end // up with ambiguity instead of reporting an actual problem with // mismatched type since each argument can have district bindings. if (auto *AFD = dyn_cast(choice)) { auto *paramList = AFD->getParameters(); auto firstParamType = paramList->get(0)->getInterfaceType(); if (elt.castTo().getParamIdx() > 0 && llvm::all_of(*paramList, [&](const ParamDecl *param) -> bool { return param->getInterfaceType()->isEqual(firstParamType); })) return true; } } } } } if (repairOutOfOrderArgumentsInBinaryFunction(*this, conversionsOrFixes, loc)) return true; // There is already a remove extraneous arguments fix recorded for this // apply arg to param locator, so let's skip the default argument mismatch. if (hasFixFor(loc, FixKind::RemoveExtraneousArguments)) return true; // If parameter is a pack, let's see if we have already recorded // either synthesized or extraneous argument fixes. if (rhs->is()) { ArrayRef tmpPath(path); // Ignore argument/parameter type conversion mismatch if we already // detected a tuple splat issue. if (hasFixFor(loc, FixKind::DestructureTupleToMatchPackExpansionParameter)) return true; // Path would end with `ApplyArgument`. auto *argsLoc = getConstraintLocator(anchor, tmpPath.drop_back()); if (hasFixFor(argsLoc, FixKind::RemoveExtraneousArguments) || hasFixFor(argsLoc, FixKind::AddMissingArguments)) return true; } // If the argument couldn't be found, this could be a default value // type mismatch. if (!simplifyLocatorToAnchor(loc)) { auto *calleeLocator = getCalleeLocator(loc); unsigned paramIdx = loc->castLastElementTo() .getParamIdx(); if (auto overload = findSelectedOverloadFor(calleeLocator)) { if (auto *decl = overload->choice.getDeclOrNull()) { if (auto paramList = decl->getParameterList()) { if (paramList->get(paramIdx)->getTypeOfDefaultExpr()) { conversionsOrFixes.push_back( IgnoreDefaultExprTypeMismatch::create(*this, lhs, rhs, loc)); break; } } } } } conversionsOrFixes.push_back( AllowArgumentMismatch::create(*this, lhs, rhs, loc)); break; } case ConstraintLocator::KeyPathRoot: { // The root mismatch is from base U? to U or a subtype of U in keypath // application so let's suggest an unwrap the optional fix. if (auto unwrapFix = UnwrapOptionalBaseKeyPathApplication::attempt( *this, lhs, rhs, getConstraintLocator(locator))) { conversionsOrFixes.push_back(unwrapFix); break; } conversionsOrFixes.push_back(AllowKeyPathRootTypeMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } case ConstraintLocator::WrappedValue: { conversionsOrFixes.push_back(AllowWrappedValueMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } case ConstraintLocator::FunctionArgument: { // Let's drop the last element which points to a single argument // and see if this is a contextual mismatch. path.pop_back(); if (path.empty() || !(path.back().getKind() == ConstraintLocator::ApplyArgToParam || path.back().getKind() == ConstraintLocator::ContextualType)) return false; if (auto argToParamElt = path.back().getAs()) { auto loc = getConstraintLocator(anchor, path); if (auto closure = getAsExpr(simplifyLocatorToAnchor(loc))) { auto closureTy = getClosureType(closure); // What we have here is a form or tuple splat with no arguments // demonstrated by following example: // // func foo(_: T, _: (T.Element) -> Int) {} // foo { 42 } // // In cases like this `T.Element` might be resolved to `Void` // which means that we have to try a single empty tuple argument // as a narrow exception to SE-0110, see `matchFunctionTypes`. // // But if `T.Element` didn't get resolved to `Void` we'd like // to diagnose this as a missing argument which can't be ignored or // a tuple is trying to be inferred as a tuple for destructuring but // contextual argument does not match(in this case we remove the extra // closure arguments). if (closureTy->getNumParams() == 0) { conversionsOrFixes.push_back(AddMissingArguments::create( *this, {SynthesizedArg{0, AnyFunctionType::Param(lhs)}}, loc)); break; } // Since this is a problem with `FunctionArgument` we know that the // contextual type only has one parameter, if closure has more than // that the fix is to remove extraneous ones. if (closureTy->getNumParams() > 1) { auto callee = getCalleeLocator(loc); if (auto overload = findSelectedOverloadFor(callee)) { auto fnType = simplifyType(overload->adjustedOpenedType) ->castTo(); auto paramIdx = argToParamElt->getParamIdx(); auto paramType = fnType->getParams()[paramIdx].getParameterType(); if (auto paramFnType = paramType->getAs()) { conversionsOrFixes.push_back(RemoveExtraneousArguments::create( *this, paramFnType, {}, loc)); break; } } } } } auto *parentLoc = getConstraintLocator(anchor, path); if (lhs->is() != rhs->is()) { // Since `FunctionArgument` as a last locator element represents // a single parameter of the function type involved in a conversion // to another function type, see `matchFunctionTypes`. If there is already // a fix for the this conversion, we can just ignore individual function // argument in-out mismatch failure by considered this fixed. if (hasFixFor(parentLoc)) return true; // We want to call matchTypes with the default decomposition options // in case there are type variables that we couldn't bind due to the // inout attribute mismatch. auto result = matchTypes(lhs->getInOutObjectType(), rhs->getInOutObjectType(), matchKind, getDefaultDecompositionOptions(TMF_ApplyingFix), locator); if (result.isSuccess()) { conversionsOrFixes.push_back(AllowInOutConversion::create(*this, lhs, rhs, getConstraintLocator(locator))); break; } } // In cases like this `FunctionArgument` as a last locator element // represents a single parameter of the function type involved in // a conversion to another function type, see `matchFunctionTypes`. if (parentLoc->isForContextualType() || parentLoc->isLastElement()) { // If either type has a placeholder, consider this fixed. if (lhs->hasPlaceholder() || rhs->hasPlaceholder()) return true; // If there is a fix associated with contextual conversion or // a function type itself, let's ignore argument failure but // increase a score. if (hasFixFor(parentLoc)) { increaseScore(SK_Fix, locator); return true; } // Since there is only one parameter let's give it a chance to diagnose // a more specific error in some situations. if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) || hasConversionOrRestriction(ConversionRestrictionKind::Existential) || hasConversionOrRestriction(ConversionRestrictionKind::Superclass)) break; conversionsOrFixes.push_back(AllowFunctionTypeMismatch::create( *this, lhs, rhs, parentLoc, /*index=*/0)); break; } break; } case ConstraintLocator::TypeParameterRequirement: case ConstraintLocator::ConditionalRequirement: { // If either type has a placeholder, consider this fixed. if (lhs->hasPlaceholder() || rhs->hasPlaceholder()) return true; // If requirement is something like `T == [Int]` let's let // type matcher a chance to match generic parameters before // recording a fix, because then we'll know exactly how many // generic parameters did not match. if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality)) break; auto *reqLoc = getConstraintLocator(locator); if (isFixedRequirement(reqLoc, rhs)) return true; // If this is a requirement on sequence of for-in statement where one // of the sides is a completely resolved dependent member, skip it // since the issue is with the conformance to `Sequence`, otherwise // dependent member would have been substituted. if (auto *UDE = getAsExpr(anchor)) { if (UDE->isImplicit() && getContextualTypePurpose(UDE->getBase()) == CTP_ForEachSequence) { if ((lhs->is() && !lhs->hasTypeVariable()) || (rhs->is() && !rhs->hasTypeVariable())) return true; } } if (auto *fix = fixRequirementFailure(*this, lhs, rhs, anchor, path)) { recordFixedRequirement(reqLoc, rhs); conversionsOrFixes.push_back(fix); } break; } case ConstraintLocator::ExistentialConstraintType: { if (lhs->hasPlaceholder() || rhs->hasPlaceholder()) return true; // If there are any restrictions/conversions left to attempt, wait. if (hasAnyRestriction()) break; // Drop the element introduced by DeepEquality matcher. path.pop_back(); // Presence of DeepEquality conversion delayed repair but since the // constraint types didn't match easier, let's retry it. return repairFailures(ExistentialType::get(lhs), ExistentialType::get(rhs), matchKind, flags, conversionsOrFixes, getConstraintLocator(anchor, path)); } case ConstraintLocator::ClosureBody: case ConstraintLocator::ClosureResult: { if (repairByInsertingExplicitCall(lhs, rhs)) break; if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) return true; // If we could record a generic arguments mismatch instead of this fix, // don't record a contextual type mismatch here. if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality)) break; auto *fix = IgnoreContextualType::create(*this, lhs, rhs, getConstraintLocator(locator)); conversionsOrFixes.push_back(fix); break; } case ConstraintLocator::ContextualType: { // If either type is a placeholder, consider this fixed if (lhs->isPlaceholder() || rhs->isPlaceholder()) return true; // If either side is not yet resolved, it's too early for this fix. if (lhs->isTypeVariableOrMember() || rhs->isTypeVariableOrMember()) break; // If there is already a fix for contextual failure, let's not // record a duplicate one. if (hasFixFor(getConstraintLocator(locator))) return true; auto purpose = getContextualTypePurpose(anchor); if (rhs->isVoid() && purpose == CTP_ReturnStmt) { conversionsOrFixes.push_back( RemoveReturn::create(*this, lhs, getConstraintLocator(locator))); return true; } if (repairByInsertingExplicitCall(lhs, rhs)) break; if (repairByAnyToAnyObjectCast(lhs, rhs)) break; if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator)) break; if (lhs->is() && !rhs->is() && isExpr(anchor)) { auto *fix = ContextualMismatch::create(*this, lhs, rhs, getConstraintLocator(locator)); conversionsOrFixes.push_back(fix); } // Solver can unwrap contextual type in an unlabeled one-element tuple // while matching type to a tuple that contains one or more pack expansion // types (because such tuples can loose their elements under substitution), // if that's the case, let's just produce a regular contextual mismatch fix. if (auto contextualType = isSingleUnlabeledElementTuple(rhs)) { rhs = contextualType; } if (purpose == CTP_Initialization && lhs->is() && rhs->is()) { auto *fix = AllowTupleTypeMismatch::create(*this, lhs, rhs, getConstraintLocator(locator)); conversionsOrFixes.push_back(fix); break; } if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) return true; // Let's wait until both sides are of the same optionality before // attempting `.rawValue` fix. if (hasConversionOrRestriction(ConversionRestrictionKind::ValueToOptional)) break; if (repairByUsingRawValueOfRawRepresentableType(lhs, rhs)) break; // If there are any restrictions here we need to wait and let // `simplifyRestrictedConstraintImpl` handle them. if (hasAnyRestriction()) break; // `lhs` - is an result type and `rhs` is a contextual type. if (repairByConstructingRawRepresentableType(lhs, rhs)) break; conversionsOrFixes.push_back(IgnoreContextualType::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } case ConstraintLocator::FunctionResult: { if (lhs->isPlaceholder() || rhs->isPlaceholder()) { recordAnyTypeVarAsPotentialHole(lhs); recordAnyTypeVarAsPotentialHole(rhs); return true; } if (auto *kpExpr = getAsExpr(anchor)) { return maybeRepairKeyPathResultFailure(kpExpr); } auto *loc = getConstraintLocator(anchor, {path.begin(), path.end() - 1}); // If this is a mismatch between contextual type and (trailing) // closure with explicitly specified result type let's record it // as contextual type mismatch. if (loc->isLastElement() || loc->isLastElement()) { auto argument = simplifyLocatorToAnchor(loc); if (isExpr(argument)) { auto *locator = getConstraintLocator(argument, ConstraintLocator::ClosureResult); if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) return true; conversionsOrFixes.push_back( IgnoreContextualType::create(*this, lhs, rhs, locator)); break; } } // Handle function result coerce expression wrong type conversion. if (isExpr(anchor)) { auto *fix = ContextualMismatch::create(*this, lhs, rhs, loc); conversionsOrFixes.push_back(fix); break; } LLVM_FALLTHROUGH; } case ConstraintLocator::Member: case ConstraintLocator::DynamicLookupResult: { // Most likely this is an attempt to use get-only subscript as mutating, // or assign a value of a result of function/member ref e.g. `foo() = 42` // or `foo.bar = 42`, or `foo.bar()! = 42`. if (repairByTreatingRValueAsLValue(rhs, lhs)) break; // `apply argument` -> `arg/param compare` -> // `@autoclosure result` -> `function result` if (path.size() > 3) { const auto &elt = path[path.size() - 2]; if (elt.getKind() == ConstraintLocator::AutoclosureResult && repairByInsertingExplicitCall(lhs, rhs)) return true; } break; } case ConstraintLocator::AutoclosureResult: { if (repairByInsertingExplicitCall(lhs, rhs)) return true; auto isPointerType = [](Type type) -> bool { return bool( type->lookThroughAllOptionalTypes()->getAnyPointerElementType()); }; // Let's see whether this is an implicit conversion to a pointer type // which is invalid in @autoclosure context e.g. from `inout`, Array // or String. if (!isPointerType(lhs) && isPointerType(rhs)) { auto result = matchTypes( lhs, rhs, ConstraintKind::ArgumentConversion, TypeMatchFlags::TMF_ApplyingFix, locator.withPathElement(ConstraintLocator::FunctionArgument)); if (result.isSuccess()) conversionsOrFixes.push_back(AllowAutoClosurePointerConversion::create( *this, lhs, rhs, getConstraintLocator(locator))); } // In situations like this: // // struct S {} // func foo(_: @autoclosure () -> S) {} // foo(S()) // // Generic type conversion mismatch is a better fix which is going to // point to the generic arguments that did not align properly. if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality)) break; conversionsOrFixes.push_back(AllowArgumentMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } case ConstraintLocator::TupleElement: { if (lhs->isPlaceholder() || rhs->isPlaceholder()) { recordAnyTypeVarAsPotentialHole(lhs); recordAnyTypeVarAsPotentialHole(rhs); return true; } if (isExpr(anchor) || isExpr(anchor)) { // If we could record a generic arguments mismatch instead of this fix, // don't record a ContextualMismatch here. if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality)) break; // We already have a fix for trying to initialize/assign an array literal // to a dictionary type. In this case elements mismatch only add extra // verbosity to the diagnostic. So let's skip the fix and only increase // the score to focus on suggesting using dictionary literal instead. path.pop_back(); auto loc = getConstraintLocator(anchor, path); if (hasFixFor(loc, FixKind::TreatArrayLiteralAsDictionary)) { increaseScore(SK_Fix, loc); return true; } conversionsOrFixes.push_back(CollectionElementContextualMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } // Drop the `tuple element` locator element so that all tuple element // mismatches within the same tuple type can be coalesced later. auto index = elt.getAs()->getIndex(); path.pop_back(); // Drop the tuple type path elements too, but extract each tuple type first. if (!path.empty() && path.back().is()) { rhs = path.back().getAs()->getType(); path.pop_back(); lhs = path.back().getAs()->getType(); path.pop_back(); } auto *tupleLocator = getConstraintLocator(locator.getAnchor(), path); // Let this fail if it's a contextual mismatch with sequence element types, // as there's a special fix for that. if (tupleLocator->isLastElement()) break; // Generic argument/requirement failures have a more general fix which // is attached to a parent type and aggregates all argument failures // into a single fix. if (tupleLocator->isLastElement() || tupleLocator->isLastElement()) break; // If the mismatch is a part of either optional-to-optional or // value-to-optional conversions, let's allow fix refer to a complete // top level type and not just a part of it. if (tupleLocator->findLast()) break; if (tupleLocator->isForContextualType()) { if (auto contextualTy = isSingleUnlabeledElementTuple(rhs)) { return repairFailures(lhs, contextualTy, matchKind, flags, conversionsOrFixes, tupleLocator); } } ConstraintFix *fix; if (tupleLocator->isLastElement()) { fix = AllowFunctionTypeMismatch::create(*this, lhs, rhs, tupleLocator, index); } else if (tupleLocator->isLastElement()) { fix = AllowArgumentMismatch::create(*this, lhs, rhs, tupleLocator); } else { fix = AllowTupleTypeMismatch::create(*this, lhs, rhs, tupleLocator, index); } conversionsOrFixes.push_back(fix); break; } case ConstraintLocator::PackElement: { path.pop_back(); if (!path.empty() && path.back().is()) path.pop_back(); if (!path.empty() && path.back().is()) path.pop_back(); return repairFailures(lhs, rhs, matchKind, flags, conversionsOrFixes, getConstraintLocator(anchor, path)); } case ConstraintLocator::PackShape: { auto *shapeLocator = getConstraintLocator(locator); // FIXME: If the anchor isn't a pack expansion, this shape requirement // came from a same-shape generic requirement, which will fail separately // with an applied requirement fix. Currently, pack shapes can themselves be // pack types with pack expansions, so matching shape types can recursively // add ShapeOf constraints. For now, skip fixing the nested ones to avoid // cascading diagnostics. if (!isExpr(shapeLocator->getAnchor())) return true; auto *fix = SkipSameShapeRequirement::create(*this, lhs, rhs, shapeLocator); conversionsOrFixes.push_back(fix); break; } case ConstraintLocator::SequenceElementType: { if (lhs->isPlaceholder() || rhs->isPlaceholder()) { recordAnyTypeVarAsPotentialHole(lhs); recordAnyTypeVarAsPotentialHole(rhs); return true; } // This is going to be diagnosed as `missing conformance`, // so no need to create duplicate fixes. if (rhs->isExistentialType()) break; // If the types didn't line up, let's allow right-hand side // of the conversion (or pattern match) to have holes. This // helps when conversion if between a type and a tuple e.g. // `Int` vs. `(_, _)`. recordAnyTypeVarAsPotentialHole(rhs); conversionsOrFixes.push_back(CollectionElementContextualMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } case ConstraintLocator::SubscriptMember: { if (repairByTreatingRValueAsLValue(lhs, rhs)) break; break; } case ConstraintLocator::Condition: { if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) return true; if (repairByInsertingExplicitCall(lhs, rhs)) return true; conversionsOrFixes.push_back(IgnoreContextualType::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } case ConstraintLocator::UnresolvedMemberChainResult: { // Ignore this mismatch if base or result is already a hole. if (lhs->isPlaceholder() || rhs->isPlaceholder()) return true; if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) return true; if (repairByTreatingRValueAsLValue(lhs, rhs)) break; // If there is a type mismatch here it's contextual e.g., // `let x: E = .foo(42)`, where `.foo` is a member of `E` // but produces an incorrect type. auto *fix = IgnoreContextualType::create(*this, lhs, rhs, getConstraintLocator(locator)); conversionsOrFixes.push_back(fix); break; } case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice: { // If this is an attempt to use readonly IUO as a destination // of an assignment e.g. // // let x: Int! = 0 // x = 42 <- `x` can be either `Int?` or `Int` but it can't be an l-value. if (lhs->is() && !rhs->is()) { auto result = matchTypes(lhs->getWithoutSpecifierType(), rhs, matchKind, TMF_ApplyingFix, locator); if (result.isSuccess()) { conversionsOrFixes.push_back( TreatRValueAsLValue::create(*this, getConstraintLocator(locator))); } } break; } case ConstraintLocator::InstanceType: { if (lhs->hasPlaceholder() || rhs->hasPlaceholder()) return true; break; } case ConstraintLocator::OptionalInjection: { if (lhs->isPlaceholder() || rhs->isPlaceholder()) return true; if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) return true; if (path.size() > 1) { path.pop_back(); if (path.back().is()) { conversionsOrFixes.push_back( CollectionElementContextualMismatch::create( *this, lhs, rhs, getConstraintLocator(anchor, path))); return true; } } break; } case ConstraintLocator::TernaryBranch: case ConstraintLocator::SingleValueStmtResult: { recordAnyTypeVarAsPotentialHole(lhs); recordAnyTypeVarAsPotentialHole(rhs); if (lhs->hasPlaceholder() || rhs->hasPlaceholder()) return true; // If there's a contextual type, let's consider it the source of truth and // produce a contextual mismatch instead of per-branch failure, because // it's a better pointer than potential then-to-else type mismatch. if (auto contextualType = getContextualType(anchor, /*forConstraint=*/false)) { auto purpose = getContextualTypePurpose(anchor); if (contextualType->isEqual(rhs)) { auto *loc = getConstraintLocator( anchor, LocatorPathElt::ContextualType(purpose)); if (hasFixFor(loc, FixKind::IgnoreContextualType)) return true; if (contextualType->isVoid() && purpose == CTP_ReturnStmt) { conversionsOrFixes.push_back(RemoveReturn::create(*this, lhs, loc)); break; } conversionsOrFixes.push_back( IgnoreContextualType::create(*this, lhs, rhs, loc)); break; } } // If there is no contextual type, this is most likely a contextual type // mismatch between the branches. conversionsOrFixes.push_back(ContextualMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } case ConstraintLocator::EnumPatternImplicitCastMatch: { // If either type is a placeholder, consider this fixed. if (lhs->isPlaceholder() || rhs->isPlaceholder()) return true; // If we're converting to an existential, we'll diagnose failures in // the conformance constraint. if (hasConversionOrRestriction(ConversionRestrictionKind::Existential)) return false; conversionsOrFixes.push_back(ContextualMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } case ConstraintLocator::PatternMatch: { auto *pattern = elt.castTo().getPattern(); // TODO: We ought to introduce a new locator element for this. bool isMemberMatch = lhs->is() && isa(pattern); // If member reference couldn't be resolved, let's allow pattern // to have holes. if (rhs->isPlaceholder() && isMemberMatch) { recordAnyTypeVarAsPotentialHole(lhs); return true; } // If either type is a placeholder, consider this fixed. if (lhs->isPlaceholder() || rhs->isPlaceholder()) return true; if (isMemberMatch) { recordAnyTypeVarAsPotentialHole(lhs); recordAnyTypeVarAsPotentialHole(rhs); conversionsOrFixes.push_back(AllowAssociatedValueMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } // `weak` declaration with an explicit non-optional type e.g. // `weak x: X = ...` where `X` is a class. if (auto *TP = dyn_cast(pattern)) { if (auto *NP = dyn_cast(TP->getSubPattern())) { auto *var = NP->getDecl(); auto ROK = ReferenceOwnership::Strong; if (auto *OA = var->getAttrs().getAttribute()) ROK = OA->get(); if (!lhs->getOptionalObjectType() && optionalityOf(ROK) == ReferenceOwnershipOptionality::Required) { conversionsOrFixes.push_back( AllowNonOptionalWeak::create(*this, getConstraintLocator(NP))); break; } } } conversionsOrFixes.push_back(ContextualMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } case ConstraintLocator::GenericArgument: { // If any of the types is a placeholder, consider it fixed. if (lhs->isPlaceholder() || rhs->isPlaceholder()) return true; // Ignoring the generic argument because we may have a generic requirement // failure e.g. `String bind T.Element`, so let's drop the generic argument // path element and recurse in repairFailures to check and potentially // record the requirement failure fix. auto genericArgElt = path.pop_back_val().castTo(); // If we have something like ... -> type req # -> pack element #, we're // solving a requirement of the form T : P where T is a type parameter pack if (!path.empty() && path.back().is()) path.pop_back(); if (!path.empty()) { if (path.back().is()) { return repairFailures(lhs, rhs, matchKind, flags, conversionsOrFixes, getConstraintLocator(anchor, path)); } if (auto argConv = path.back().getAs()) { auto argIdx = argConv->getArgIdx(); auto paramIdx = argConv->getParamIdx(); auto *argLoc = getConstraintLocator(anchor, path); if (auto overload = findSelectedOverloadFor(getCalleeLocator(argLoc))) { auto *overloadTy = simplifyType(overload->boundType)->castTo(); auto *argList = getArgumentList(argLoc); ASSERT(argList); conversionsOrFixes.push_back(AllowArgumentMismatch::create( *this, getType(argList->getExpr(argIdx)), overloadTy->getParams()[paramIdx].getPlainType(), argLoc)); return true; } } } // When the solver sets `TMF_MatchingGenericArguments` it means // that it's matching generic argument pairs to identify any mismatches // as part of larger matching of two generic types. Letting this // fail results in a single fix that aggregates all mismatch locations. // // Types are not always resolved enough to enable that which means // that the comparison should be delayed, which brings us here - a // standalone constraint that represents such a match, in such cases // we create a fix per mismatch location and coalesce them during // diagnostics. if (flags.contains(TMF_MatchingGenericArguments)) break; if (hasAnyRestriction()) break; Type fromType; Type toType; if (path.size() >= 2) { if (path[path.size() - 2].is()) { fromType = path[path.size() - 2] .castTo() .getType(); } if (path[path.size() - 1].is()) { toType = path[path.size() - 1] .castTo() .getType(); } } if (!fromType || !toType) break; Type fromObjectType, toObjectType; unsigned fromUnwraps, toUnwraps; std::tie(fromObjectType, fromUnwraps) = getObjectTypeAndNumUnwraps(lhs); std::tie(toObjectType, toUnwraps) = getObjectTypeAndNumUnwraps(rhs); // If the bound contextual type is more optional than the binding type, then // propogate binding type to contextual type and attempt to solve. if (fromUnwraps < toUnwraps) { (void)matchTypes(fromObjectType, toObjectType, ConstraintKind::Bind, TMF_ApplyingFix, locator); } // Drop both `GenericType` elements. path.pop_back(); path.pop_back(); ConstraintFix *fix = nullptr; auto *fixLoc = getConstraintLocator(anchor, path); if (fixLoc->isLastElement()) { fix = fixRequirementFailure(*this, fromType, toType, anchor, path); } else if (fixLoc->isLastElement()) { return repairFailures(lhs, rhs, matchKind, flags, conversionsOrFixes, fixLoc); } else if (!lhs->mayHaveSuperclass() && rhs->isAnyObject()) { fix = AllowNonClassTypeToConvertToAnyObject::create(*this, fromType, fixLoc); } else { fix = GenericArgumentsMismatch::create( *this, fromType, toType, {genericArgElt.getIndex()}, fixLoc); } if (!fix) break; conversionsOrFixes.push_back(fix); return true; } case ConstraintLocator::ResultBuilderBodyResult: { // If result type of the body couldn't be determined // there is going to be other fix available to diagnose // the underlying issue. if (lhs->isPlaceholder()) return true; conversionsOrFixes.push_back(ContextualMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; } case ConstraintLocator::GlobalActorType: { // Drop global actor element as it servers only to indentify the global // actor matching. path.pop_back(); conversionsOrFixes.push_back(AllowGlobalActorMismatch::create( *this, lhs, rhs, getConstraintLocator(anchor, path))); break; } case ConstraintLocator::CoercionOperand: { auto *coercion = castToExpr(anchor); // Coercion from T.Type to T.Protocol. if (hasConversionOrRestriction( ConversionRestrictionKind::MetatypeToExistentialMetatype)) return false; if (hasConversionOrRestriction(ConversionRestrictionKind::Superclass)) return false; // Let's check whether the sub-expression is an optional type which // is possible to unwrap (either by force or `??`) to satisfy the cast, // otherwise we'd have to fallback to force downcast. if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, getConstraintLocator(coercion->getSubExpr()))) return true; // If the result type of the coercion has an value to optional conversion // we can instead suggest the conditional downcast as it is safer in // situations like conditional binding. auto useConditionalCast = llvm::any_of(ConstraintRestrictions, [&](const auto &restriction) { Type type1, type2; std::tie(type1, type2) = restriction.first; auto restrictionKind = restriction.second; if (restrictionKind != ConversionRestrictionKind::ValueToOptional) return false; return rhs->isEqual(type1); }); // Repair a coercion ('as') with a runtime checked cast ('as!' or 'as?'). if (auto *coerceToCheckCastFix = CoerceToCheckedCast::attempt(*this, lhs, rhs, useConditionalCast, getConstraintLocator(locator))) { conversionsOrFixes.push_back(coerceToCheckCastFix); return true; } // If it has a deep equality restriction, defer the diagnostic to // GenericMismatch. if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) && !hasConversionOrRestriction( ConversionRestrictionKind::OptionalToOptional)) { return false; } if (hasConversionOrRestriction(ConversionRestrictionKind::Existential)) return false; auto *fix = ContextualMismatch::create(*this, lhs, rhs, getConstraintLocator(locator)); conversionsOrFixes.push_back(fix); return true; } case ConstraintLocator::KeyPathValue: { if (maybeRepairKeyPathResultFailure(getAsExpr(anchor))) return true; break; } default: break; } return !conversionsOrFixes.empty(); } static bool isTupleWithUnresolvedPackExpansion(Type type) { if (auto *tuple = type->getAs()) { return llvm::any_of(tuple->getElements(), [&](const TupleTypeElt &elt) { if (auto typeVar = elt.getType()->getAs()) return typeVar->getImpl().isPackExpansion(); return false; }); } return false; } ConstraintSystem::TypeMatchResult ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto origType1 = type1; auto origType2 = type2; // If we have type variables that have been bound to fixed types, look through // to the fixed type. type1 = getFixedTypeRecursive(type1, flags, kind == ConstraintKind::Equal); type2 = getFixedTypeRecursive(type2, flags, kind == ConstraintKind::Equal); auto desugar1 = type1->getDesugaredType(); auto desugar2 = type2->getDesugaredType(); // If both sides are dependent members without type variables, it's // possible that base type is incorrect e.g. `Foo.Element` where `Foo` // is a concrete type substituted for generic parameter, // so checking equality here would lead to incorrect behavior, // let's defer it until later proper check. if (!(desugar1->is() && desugar2->is())) { // If the types are obviously equivalent, we're done. if (desugar1->isEqual(desugar2) && !isa(desugar2)) { return getTypeMatchSuccess(); } } // Local function that should be used to produce the return value whenever // this function was unable to resolve the constraint. It should be used // within \c matchTypes() as // // return formUnsolvedResult(); // // along any unsolved path. No other returns should produce // SolutionKind::Unsolved or inspect TMF_GenerateConstraints. auto formUnsolvedResult = [&] { // If we're supposed to generate constraints (i.e., this is a // newly-generated constraint), do so now. if (flags.contains(TMF_GenerateConstraints)) { // Add a new constraint between these types. We consider the current // type-matching problem to the "solved" by this addition, because // this new constraint will be solved at a later point. // Obviously, this must not happen at the top level, or the // algorithm would not terminate. addUnsolvedConstraint(Constraint::create(*this, kind, type1, type2, getConstraintLocator(locator))); return getTypeMatchSuccess(); } return getTypeMatchAmbiguous(); }; auto *typeVar1 = dyn_cast(desugar1); auto *typeVar2 = dyn_cast(desugar2); // If either (or both) types are type variables, unify the type variables. if (typeVar1 || typeVar2) { // Handle the easy case of both being type variables, and being // identical, first. if (typeVar1 && typeVar2) { auto rep1 = getRepresentative(typeVar1); auto rep2 = getRepresentative(typeVar2); if (rep1 == rep2) { // We already merged these two types, so this constraint is // trivially solved. return getTypeMatchSuccess(); } } switch (kind) { case ConstraintKind::Bind: case ConstraintKind::BindToPointerType: case ConstraintKind::Equal: { if (typeVar1 && typeVar2) { auto rep1 = getRepresentative(typeVar1); auto rep2 = getRepresentative(typeVar2); // Pack expansion variables cannot be merged because // they involve other type variables. if (rep1->getImpl().isPackExpansion() || rep2->getImpl().isPackExpansion()) return formUnsolvedResult(); // If exactly one of the type variables can bind to an lvalue, we // can't merge these two type variables. if (kind == ConstraintKind::Equal && rep1->getImpl().canBindToLValue() != rep2->getImpl().canBindToLValue()) return formUnsolvedResult(); // Merge the equivalence classes corresponding to these two variables. mergeEquivalenceClasses(rep1, rep2, /*updateWorkList=*/true); return getTypeMatchSuccess(); } // If type variable represents a key path value type, defer binding it to // contextual type in diagnostic mode. We want it to be bound from the // last key path component to help with diagnostics. if (shouldAttemptFixes()) { if (typeVar1 && typeVar1->getImpl().isKeyPathValue() && !flags.contains(TMF_BindingTypeVariable)) return formUnsolvedResult(); } assert((type1->is() != type2->is()) && "Expected a type variable and a non type variable!"); auto *typeVar = typeVar1 ? typeVar1 : typeVar2; auto type = typeVar1 ? type2 : type1; return matchTypesBindTypeVar(typeVar, type, kind, flags, locator, formUnsolvedResult); } case ConstraintKind::BindParam: { if (typeVar2 && !typeVar1) { // Simplify the left-hand type and perform the "occurs" check. auto rep2 = getRepresentative(typeVar2); type1 = simplifyType(type1, flags); if (!isBindable(typeVar2, type1)) return formUnsolvedResult(); if (auto *iot = type1->getAs()) { if (!rep2->getImpl().canBindToLValue()) return getTypeMatchFailure(locator); assignFixedType(rep2, LValueType::get(iot->getObjectType())); } else { assignFixedType(rep2, type1); } return getTypeMatchSuccess(); } else if (typeVar1 && !typeVar2) { // Simplify the right-hand type and perform the "occurs" check. auto rep1 = getRepresentative(typeVar1); type2 = simplifyType(type2, flags); if (!isBindable(rep1, type2)) return formUnsolvedResult(); if (auto *lvt = type2->getAs()) { if (!rep1->getImpl().canBindToInOut()) return getTypeMatchFailure(locator); assignFixedType(rep1, InOutType::get(lvt->getObjectType())); } else { assignFixedType(rep1, type2); } return getTypeMatchSuccess(); } if (typeVar1 && typeVar2) { auto rep1 = getRepresentative(typeVar1); auto rep2 = getRepresentative(typeVar2); // Pack expansion variables cannot be merged because // they involve other type variables. if (rep1->getImpl().isPackExpansion() || rep2->getImpl().isPackExpansion()) return formUnsolvedResult(); if (!rep1->getImpl().canBindToInOut() || !rep2->getImpl().canBindToLValue()) { // Merge the equivalence classes corresponding to these two variables. mergeEquivalenceClasses(rep1, rep2, /*updateWorkList=*/true); return getTypeMatchSuccess(); } } return formUnsolvedResult(); } case ConstraintKind::Subtype: case ConstraintKind::Conversion: case ConstraintKind::ArgumentConversion: case ConstraintKind::OperatorArgumentConversion: { if (typeVar1) { // Performance optimization: Propagate fully or partially resolved // contextual type down into the body of result builder transformed // closure by eagerly binding intermediate body result type to the // contextual one. This helps to determine when closure body could be // solved early. // // TODO: This could be extended to cover all multi-statement closures. // // See \c BindingSet::favoredOverConjunction for more details. if (!typeVar2 && locator.endsWith()) { SmallVector path; auto anchor = locator.getLocatorParts(path); // Drop `FunctionResult` element. path.pop_back(); ClosureExpr *closure = nullptr; { // This avoids a new locator allocation. SourceRange range; ArrayRef scratchPath(path); simplifyLocator(anchor, scratchPath, range); if (scratchPath.empty()) closure = getAsExpr(anchor); } if (closure && !closure->hasExplicitResultType() && getAppliedResultBuilderTransform(closure)) { return matchTypesBindTypeVar(typeVar1, type2, ConstraintKind::Equal, flags, locator, formUnsolvedResult); } } } return formUnsolvedResult(); } case ConstraintKind::ApplicableFunction: case ConstraintKind::DynamicCallableApplicableFunction: case ConstraintKind::BindOverload: case ConstraintKind::BridgingConversion: case ConstraintKind::CheckedCast: case ConstraintKind::SubclassOf: case ConstraintKind::NonisolatedConformsTo: case ConstraintKind::ConformsTo: case ConstraintKind::TransitivelyConformsTo: case ConstraintKind::Defaultable: case ConstraintKind::Disjunction: case ConstraintKind::Conjunction: case ConstraintKind::DynamicTypeOf: case ConstraintKind::EscapableFunctionOf: case ConstraintKind::OpenedExistentialOf: case ConstraintKind::KeyPath: case ConstraintKind::KeyPathApplication: case ConstraintKind::LiteralConformsTo: case ConstraintKind::OptionalObject: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: case ConstraintKind::ValueWitness: case ConstraintKind::OneWayEqual: case ConstraintKind::FallbackType: case ConstraintKind::UnresolvedMemberChainBase: case ConstraintKind::PropertyWrapper: case ConstraintKind::SyntacticElement: case ConstraintKind::BindTupleOfFunctionParams: case ConstraintKind::PackElementOf: case ConstraintKind::ShapeOf: case ConstraintKind::ExplicitGenericArguments: case ConstraintKind::SameShape: case ConstraintKind::MaterializePackExpansion: case ConstraintKind::LValueObject: llvm_unreachable("Not a relational constraint"); } } // If one of the types is a member type of a type variable type, // there's nothing we can do. if (desugar1->isTypeVariableOrMember() || desugar2->isTypeVariableOrMember()) { return formUnsolvedResult(); } // If the original type on one side consisted of a tuple type with // unresolved pack expansion(s), let's make sure that both sides are // tuples to enable proper pack matching for situations like: // // `Int (_: $T3)` // where `$T3` is pack expansion of pattern type `$T2` // // `Int` should be wrapped in a one-element tuple to make sure // that tuple matcher can form a pack expansion type that would // match `$T3` and propagate `Pack{Int}` to `$T2`. // // This is also important for situations like: `$T2 conv (Int, $T_exp)` // because expansion could be defaulted to an empty pack which means // that under substitution that element would disappear and the type // would be just `(Int)`. // // Notable exceptions here are: `Any` which doesn't require wrapping and // would be handled by an existential promotion in cases where it's allowed, // and `Optional` which would be handled by optional injection. if (isTupleWithUnresolvedPackExpansion(origType1) || isTupleWithUnresolvedPackExpansion(origType2)) { if (isa(desugar1) != isa(desugar2) && !isa(desugar1) && !isa(desugar2) && !desugar1->getOptionalObjectType() && !desugar2->getOptionalObjectType() && !desugar1->isAny() && !desugar2->isAny()) { return matchTypes( desugar1->is() ? type1 : TupleType::get({type1}, getASTContext()), desugar2->is() ? type2 : TupleType::get({type2}, getASTContext()), kind, flags, locator); } } llvm::SmallVector conversionsOrFixes; // Decompose parallel structure. TypeMatchOptions subflags = getDefaultDecompositionOptions(flags) - TMF_ApplyingFix; if (desugar1->getKind() == desugar2->getKind()) { switch (desugar1->getKind()) { #define SUGARED_TYPE(id, parent) case TypeKind::id: #define TYPE(id, parent) #include "swift/AST/TypeNodes.def" llvm_unreachable("Type has not been desugared completely"); #define ARTIFICIAL_TYPE(id, parent) case TypeKind::id: #define TYPE(id, parent) #include "swift/AST/TypeNodes.def" llvm_unreachable("artificial type in constraint"); case TypeKind::BuiltinTuple: llvm_unreachable("BuiltinTupleType in constraint"); // Note: Mismatched builtin types fall through to the TypeKind::Error // case below. #define BUILTIN_TYPE(id, parent) case TypeKind::id: #define TYPE(id, parent) #include "swift/AST/TypeNodes.def" case TypeKind::Error: case TypeKind::Unresolved: return getTypeMatchFailure(locator); case TypeKind::Placeholder: { // If it's allowed to attempt fixes, let's delegate // decision to `repairFailures`, since depending on // locator we might either ignore such a mismatch, // or record a specialized fix. if (shouldAttemptFixes()) break; return getTypeMatchFailure(locator); } case TypeKind::GenericTypeParam: llvm_unreachable("unmapped dependent type in type checker"); case TypeKind::TypeVariable: llvm_unreachable("type variables should have already been handled by now"); case TypeKind::DependentMember: { // If types are identical, let's consider this constraint solved // even though they are dependent members, they would be resolved // to the same concrete type. if (desugar1->isEqual(desugar2)) return getTypeMatchSuccess(); if (shouldAttemptFixes()) { if (!desugar1->hasTypeVariable() && !desugar2->hasTypeVariable()) { auto *loc = getConstraintLocator(locator); auto *fix = loc->isLastElement() ? fixRequirementFailure(*this, type1, type2, loc->getAnchor(), loc->getPath()) : ContextualMismatch::create(*this, type1, type2, loc); if (!fix || recordFix(fix)) return getTypeMatchFailure(locator); return getTypeMatchSuccess(); } } // If one of the dependent member types has no type variables, // this comparison is effectively illformed, because dependent // member couldn't be simplified down to the actual type, and // we wouldn't be able to solve this constraint, so let's just fail. // This should only happen outside of diagnostic mode, as otherwise the // member is replaced by a placeholder in simplifyType. if (!desugar1->hasTypeVariable() || !desugar2->hasTypeVariable()) return getTypeMatchFailure(locator); // Nothing we can solve yet, since we need to wait until // type variables will get resolved. return formUnsolvedResult(); } case TypeKind::Module: case TypeKind::PrimaryArchetype: case TypeKind::PackArchetype: case TypeKind::ElementArchetype: { // Give `repairFailures` a chance to fix the problem. if (shouldAttemptFixes()) break; // If two module types or archetypes were not already equal, there's // nothing more we can do. return getTypeMatchFailure(locator); } case TypeKind::Tuple: { // FIXME: TuplePackMatcher doesn't correctly handle matching two // abstract contextual tuple types in a generic context. if (simplifyType(desugar1)->isEqual(simplifyType(desugar2))) return getTypeMatchSuccess(); // If the tuple has consecutive pack expansions, packs must be // resolved before matching. auto delayMatching = [](TupleType *tuple) { bool afterPack = false; for (auto element : tuple->getElements()) { if (afterPack && !element.hasName()) { SmallPtrSet typeVars; element.getType()->getTypeVariables(typeVars); bool hasUnresolvedPack = llvm::any_of(typeVars, [](auto *tv) { return tv->getImpl().canBindToPack(); }); if (hasUnresolvedPack) return true; } // Delay matching if one of the elements is unresolved pack // expansion represented by a type variable. if (auto *typeVar = element.getType()->getAs()) { if (typeVar->getImpl().isPackExpansion()) return true; } afterPack = element.getType()->is(); } return false; }; auto *tuple1 = cast(desugar1); auto *tuple2 = cast(desugar2); if (delayMatching(tuple1) || delayMatching(tuple2)) { return formUnsolvedResult(); } // Closure result is allowed to convert to Void in certain circumstances, // let's forego tuple matching because it is guaranteed to fail and jump // to `() -> T` to `() -> Void` rule. if (locator.endsWith()) { if (containsPackExpansionType(tuple1) && tuple2->isVoid()) break; } // Add each tuple type to the locator before matching the element types. // This is useful for diagnostics, because the error message can use the // full tuple type for several element mismatches. Use the original types // to preserve sugar such as typealiases. auto tmpTupleLoc = locator.withPathElement(LocatorPathElt::TupleType(type1)); auto tupleLoc = tmpTupleLoc.withPathElement(LocatorPathElt::TupleType(type2)); auto result = matchTupleTypes(cast(desugar1), cast(desugar2), kind, subflags, tupleLoc); if (result != SolutionKind::Error) return result; // FIXME: All cases in this switch should go down to the fix logic // to give repairFailures() a chance to run, but this breaks stuff // right now. break; } case TypeKind::Enum: case TypeKind::Struct: case TypeKind::Class: { auto nominal1 = cast(desugar1); auto nominal2 = cast(desugar2); if (nominal1->getDecl() == nominal2->getDecl()) conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality); // Check for CF <-> ObjectiveC bridging. if (isa(desugar1) && kind >= ConstraintKind::Subtype) { auto class1 = cast(nominal1->getDecl()); auto class2 = cast(nominal2->getDecl()); // CF -> Objective-C via toll-free bridging. if (class1->getForeignClassKind() == ClassDecl::ForeignKind::CFType && class2->getForeignClassKind() != ClassDecl::ForeignKind::CFType && class1->getAttrs().hasAttribute()) { conversionsOrFixes.push_back( ConversionRestrictionKind::CFTollFreeBridgeToObjC); } // Objective-C -> CF via toll-free bridging. if (class2->getForeignClassKind() == ClassDecl::ForeignKind::CFType && class1->getForeignClassKind() != ClassDecl::ForeignKind::CFType && class2->getAttrs().hasAttribute()) { conversionsOrFixes.push_back( ConversionRestrictionKind::ObjCTollFreeBridgeToCF); } } if (kind >= ConstraintKind::Subtype && nominal1->getDecl() != nominal2->getDecl() && ((nominal1->isCGFloat() || nominal2->isCGFloat()) && (nominal1->isDouble() || nominal2->isDouble()))) { ConstraintLocatorBuilder location{locator}; // Look through all value-to-optional promotions to allow // conversions like Double -> CGFloat?? and vice versa. // T -> Optional if (location.endsWith()) { SmallVector path; auto anchor = location.getLocatorParts(path); // An attempt at Double/CGFloat conversion through // optional chaining. This is not supported at the // moment because solution application doesn't know // how to map Double to/from CGFloat through optionals. if (isExpr(anchor)) { if (!shouldAttemptFixes()) return getTypeMatchFailure(locator); conversionsOrFixes.push_back(ContextualMismatch::create( *this, nominal1, nominal2, getConstraintLocator(locator))); break; } // Drop all of the applied `value-to-optional` promotions. path.erase(llvm::remove_if( path, [](const LocatorPathElt &elt) { return elt.is(); }), path.end()); location = getConstraintLocator(anchor, path); } // Support implicit Double<->CGFloat conversions only for // something which could be directly represented in the AST // e.g. argument-to-parameter, contextual conversions etc. if (!location.trySimplifyToExpr()) { return getTypeMatchFailure(locator); } SmallVector path; auto anchor = location.getLocatorParts(path); // Try implicit CGFloat conversion only if: // - This is not: // - an explicit call to a CGFloat initializer; // - an explicit coercion; // - a runtime type check (via `is` expression); // - a checked or conditional cast; // - This is a first type such conversion is attempted for // for a given path (AST element). auto isCGFloatInit = [&](ASTNode location) { if (auto *call = getAsExpr(location)) { if (auto *typeExpr = dyn_cast(call->getFn())) { return getInstanceType(typeExpr)->isCGFloat(); } } return false; }; auto isCoercionOrCast = [](ASTNode anchor, ArrayRef path) { // E.g. contextual conversion from coercion/cast // to some other type. if (!(path.empty() || path.back().is())) return false; return isExpr(anchor) || isExpr(anchor) || isExpr(anchor) || isExpr(anchor); }; if (!isCGFloatInit(anchor) && !isCoercionOrCast(anchor, path) && llvm::none_of(path, [&](const LocatorPathElt &rawElt) { if (auto elt = rawElt.getAs()) { auto convKind = elt->getConversionKind(); return convKind == ConversionRestrictionKind::DoubleToCGFloat || convKind == ConversionRestrictionKind::CGFloatToDouble; } return false; })) { conversionsOrFixes.push_back( desugar1->isCGFloat() ? ConversionRestrictionKind::CGFloatToDouble : ConversionRestrictionKind::DoubleToCGFloat); } } break; } case TypeKind::DynamicSelf: // FIXME: Deep equality? What is the rule between two DynamicSelfs? break; case TypeKind::Protocol: // Nothing to do here; try existential and user-defined conversions below. break; case TypeKind::Metatype: case TypeKind::ExistentialMetatype: { auto meta1 = cast(desugar1); auto meta2 = cast(desugar2); // A.Type < B.Type if A < B and both A and B are classes. // P.Type < Q.Type if P < Q, both P and Q are protocols, and P.Type // and Q.Type are both existential metatypes auto subKind = std::min(kind, ConstraintKind::Subtype); // If instance types can't have a subtype relationship // it means that such types can be simply equated. auto instanceType1 = meta1->getInstanceType(); auto instanceType2 = meta2->getInstanceType(); if (isa(meta1) && !(instanceType1->mayHaveSuperclass() && instanceType2->getClassOrBoundGenericClass())) { subKind = ConstraintKind::Bind; } auto result = matchTypes(instanceType1, instanceType2, subKind, subflags, locator.withPathElement(ConstraintLocator::InstanceType)); // If matching of the instance types resulted in the failure make sure // to give `repairFailure` a chance to run to attempt to fix the issue. if (shouldAttemptFixes() && result.isFailure()) break; return result; } case TypeKind::Function: { auto func1 = cast(desugar1); auto func2 = cast(desugar2); auto result = matchFunctionTypes(func1, func2, kind, flags, locator); if (shouldAttemptFixes() && result.isFailure()) break; return result; } case TypeKind::GenericFunction: llvm_unreachable("Polymorphic function type should have been opened"); case TypeKind::Existential: case TypeKind::ProtocolComposition: case TypeKind::ParameterizedProtocol: switch (kind) { case ConstraintKind::Equal: case ConstraintKind::Bind: case ConstraintKind::BindParam: // If we are matching types for equality, we might still have // type variables inside the protocol composition's superclass // constraint. if (desugar1->getKind() == desugar2->getKind()) conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality); break; default: // Subtype constraints where the RHS is an existential type are // handled below. break; } break; case TypeKind::LValue: if (kind == ConstraintKind::BindParam) return getTypeMatchFailure(locator); return matchTypes(cast(desugar1)->getObjectType(), cast(desugar2)->getObjectType(), ConstraintKind::Bind, subflags, locator.withPathElement( ConstraintLocator::LValueConversion)); case TypeKind::InOut: if (kind == ConstraintKind::BindParam) return getTypeMatchFailure(locator); if (kind == ConstraintKind::OperatorArgumentConversion) { conversionsOrFixes.push_back( RemoveAddressOf::create(*this, type1, type2, getConstraintLocator(locator))); break; } return matchTypes(cast(desugar1)->getObjectType(), cast(desugar2)->getObjectType(), ConstraintKind::Bind, subflags, locator.withPathElement(ConstraintLocator::LValueConversion)); case TypeKind::UnboundGeneric: llvm_unreachable("Unbound generic type should have been opened"); case TypeKind::BoundGenericClass: case TypeKind::BoundGenericEnum: case TypeKind::BoundGenericStruct: { auto bound1 = cast(desugar1); auto bound2 = cast(desugar2); if (bound1->getDecl() == bound2->getDecl()) conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality); break; } // Opaque archetypes are globally bound, so we can match them for deep // equality. case TypeKind::OpaqueTypeArchetype: { auto opaque1 = cast(desugar1); auto opaque2 = cast(desugar2); if (opaque1->getDecl() == opaque2->getDecl()) { conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality); } break; } case TypeKind::ExistentialArchetype: { auto opened1 = cast(desugar1); auto opened2 = cast(desugar2); // If they have the same interface type and UUID, two ExistentialArchetypeTypes // match if their generic arguments do as well. if (opened1->getInterfaceType()->isEqual(opened2->getInterfaceType()) && opened1->getGenericEnvironment()->getOpenedExistentialUUID() == opened2->getGenericEnvironment()->getOpenedExistentialUUID()) { conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality); } break; } case TypeKind::Pack: { auto tmpPackLoc = locator.withPathElement(LocatorPathElt::PackType(type1)); auto packLoc = tmpPackLoc.withPathElement(LocatorPathElt::PackType(type2)); auto result = matchPackTypes(cast(desugar1), cast(desugar2), kind, subflags, packLoc); // Let `repairFailures` attempt to "fix" this. if (shouldAttemptFixes() && result.isFailure()) break; return result; } case TypeKind::PackExpansion: { auto expansion1 = cast(desugar1); auto expansion2 = cast(desugar2); return matchPackExpansionTypes(expansion1, expansion2, kind, subflags, locator); } case TypeKind::PackElement: { auto pack1 = cast(desugar1)->getPackType(); auto pack2 = cast(desugar2)->getPackType(); return matchTypes(pack1, pack2, kind, subflags, locator); } case TypeKind::ErrorUnion: break; case TypeKind::Integer: if (shouldAttemptFixes()) break; // If we're asking if two integer types are the same, then we know they // aren't. return getTypeMatchFailure(locator); } } if (kind == ConstraintKind::BindToPointerType) { if (desugar2->isEqual(getASTContext().TheEmptyTupleType)) return getTypeMatchSuccess(); } if (kind == ConstraintKind::BindParam) { if (auto *iot = dyn_cast(desugar1)) { if (auto *lvt = dyn_cast(desugar2)) { return matchTypes(iot->getObjectType(), lvt->getObjectType(), ConstraintKind::Bind, subflags, locator.withPathElement( ConstraintLocator::LValueConversion)); } } } if (kind >= ConstraintKind::Conversion) { // An lvalue of type T1 can be converted to a value of type T2 so long as // T1 is convertible to T2 (by loading the value). Note that we cannot get // a value of inout type as an lvalue though. if (type1->is() && !type2->is()) { auto result = matchTypes(type1->getWithoutSpecifierType(), type2, kind, subflags, locator); if (result.isSuccess() || !shouldAttemptFixes()) return result; } } if (kind >= ConstraintKind::Subtype) { // Subclass-to-superclass conversion. if (type1->mayHaveSuperclass() && type2->getClassOrBoundGenericClass() && type1->getClassOrBoundGenericClass() != type2->getClassOrBoundGenericClass()) { conversionsOrFixes.push_back(ConversionRestrictionKind::Superclass); } // Existential-to-superclass conversion. if (type1->isClassExistentialType() && type2->getClassOrBoundGenericClass()) { conversionsOrFixes.push_back(ConversionRestrictionKind::Superclass); } // Metatype-to-existential-metatype conversion. // // Equivalent to a conformance relation on the instance types. if (type1->is() && type2->is()) { conversionsOrFixes.push_back( ConversionRestrictionKind::MetatypeToExistentialMetatype); } // Existential-metatype-to-superclass-metatype conversion. if (type2->is()) { if (auto *meta1 = type1->getAs()) { if (meta1->getInstanceType()->isClassExistentialType()) { conversionsOrFixes.push_back( ConversionRestrictionKind::ExistentialMetatypeToMetatype); } } } // Concrete value to existential conversion. if (!type1->is() && type2->isExistentialType()) { // Penalize conversions to Any. if (kind >= ConstraintKind::Conversion && type2->isAny()) increaseScore(ScoreKind::SK_EmptyExistentialConversion, locator); conversionsOrFixes.push_back(ConversionRestrictionKind::Existential); } // T -> AnyHashable. if (desugar2->isAnyHashable()) { // Don't allow this in operator contexts or we'll end up allowing // 'T() == U()' for unrelated T and U that just happen to be Hashable. // We can remove this special case when we implement operator hiding. if (!type1->is() && kind != ConstraintKind::OperatorArgumentConversion) { conversionsOrFixes.push_back( ConversionRestrictionKind::HashableToAnyHashable); } } // Metatype to object conversion. // // Class and protocol metatypes are interoperable with certain Objective-C // runtime classes, but only when ObjC interop is enabled. // Foreign reference types do *not* conform to AnyObject. if (type1->isForeignReferenceType() && type2->isAnyObject()) return getTypeMatchFailure(locator); if (getASTContext().LangOpts.EnableObjCInterop) { // These conversions are between concrete types that don't need further // resolution, so we can consider them immediately solved. auto addSolvedRestrictedConstraint = [&](ConversionRestrictionKind restriction) -> TypeMatchResult { addRestrictedConstraint(ConstraintKind::Subtype, restriction, type1, type2, locator); return getTypeMatchSuccess(); }; if (auto meta1 = type1->getAs()) { if (meta1->getInstanceType()->mayHaveSuperclass() && type2->isAnyObject()) { increaseScore(ScoreKind::SK_UserConversion, locator); 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; }; auto constraintType = meta1->getInstanceType(); if (auto existential = constraintType->getAs()) constraintType = existential->getConstraintType(); if (auto protoTy = constraintType->getAs()) { if (protoTy->getDecl()->isObjC() && isProtocolClassType(type2)) { increaseScore(ScoreKind::SK_UserConversion, locator); return addSolvedRestrictedConstraint( ConversionRestrictionKind::ProtocolMetatypeToProtocolClass); } } } if (auto meta1 = type1->getAs()) { // Class-constrained existential metatypes can be converted to AnyObject. if (meta1->getInstanceType()->isClassExistentialType() && type2->isAnyObject()) { increaseScore(ScoreKind::SK_UserConversion, locator); return addSolvedRestrictedConstraint( ConversionRestrictionKind::ExistentialMetatypeToAnyObject); } } } // Special implicit nominal conversions. if (!type1->is()) { // Array -> Array. if (desugar1->isArray() && desugar2->isArray()) { conversionsOrFixes.push_back(ConversionRestrictionKind::ArrayUpcast); // Dictionary -> Dictionary. } else if (isDictionaryType(desugar1) && isDictionaryType(desugar2)) { conversionsOrFixes.push_back( ConversionRestrictionKind::DictionaryUpcast); // Set -> Set. } else if (isSetType(desugar1) && isSetType(desugar2)) { conversionsOrFixes.push_back( ConversionRestrictionKind::SetUpcast); } } } if (kind >= ConstraintKind::Conversion) { // It is never legal to form an autoclosure that results in these // implicit conversions to pointer types. bool isAutoClosureArgument = locator.isForAutoclosureResult(); // Pointer arguments can be converted from pointer-compatible types. if (kind >= ConstraintKind::ArgumentConversion) { Type unwrappedType2 = type2; bool type2IsOptional = false; if (Type unwrapped = type2->getOptionalObjectType()) { type2IsOptional = true; unwrappedType2 = unwrapped; } PointerTypeKind pointerKind; if (Type pointeeTy = unwrappedType2->getAnyPointerElementType(pointerKind)) { switch (pointerKind) { case PTK_UnsafeRawPointer: case PTK_UnsafeMutableRawPointer: case PTK_UnsafePointer: case PTK_UnsafeMutablePointer: // UnsafeMutablePointer can be converted from an inout reference to a // scalar or array. if (auto inoutType1 = dyn_cast(desugar1)) { if (!isAutoClosureArgument) { auto inoutBaseType = getFixedTypeRecursive( inoutType1->getInOutObjectType(), /*wantRValue=*/true); // Wait until the base type of `inout` is sufficiently resolved // before making any assessments regarding conversions. if (inoutBaseType->isTypeVariableOrMember()) return formUnsolvedResult(); auto baseIsArray = inoutBaseType->isArray(); if (baseIsArray) conversionsOrFixes.push_back( ConversionRestrictionKind::ArrayToPointer); // Only try an inout-to-pointer conversion if we know it's not // an array being converted to a raw pointer type. Such // conversions can only use array-to-pointer. if (!baseIsArray || !isRawPointerKind(pointerKind)) { conversionsOrFixes.push_back( ConversionRestrictionKind::InoutToPointer); // If regular inout-to-pointer conversion doesn't work, // let's try C pointer conversion that has special semantics // for imported declarations. if (isArgumentOfImportedDecl(locator)) { conversionsOrFixes.push_back( ConversionRestrictionKind::InoutToCPointer); } } } } // Operators cannot use these implicit conversions. if (kind == ConstraintKind::ArgumentConversion) { // We can potentially convert from an UnsafeMutablePointer // of a different type, if we're a void pointer. Type unwrappedType1 = type1; bool type1IsOptional = false; if (Type unwrapped = type1->getOptionalObjectType()) { type1IsOptional = true; unwrappedType1 = unwrapped; } // Don't handle normal optional-related conversions here. if (unwrappedType1->isEqual(unwrappedType2)) break; PointerTypeKind type1PointerKind; bool type1IsPointer{ unwrappedType1->getAnyPointerElementType(type1PointerKind)}; bool optionalityMatches = !type1IsOptional || type2IsOptional; if (type1IsPointer && optionalityMatches) { if (type1PointerKind == PTK_UnsafeMutablePointer) { // Favor an UnsafeMutablePointer-to-UnsafeMutablePointer // conversion. if (type1PointerKind != pointerKind) increaseScore(ScoreKind::SK_ValueToPointerConversion, locator); conversionsOrFixes.push_back( ConversionRestrictionKind::PointerToPointer); } // UnsafeMutableRawPointer -> UnsafeRawPointer else if (type1PointerKind == PTK_UnsafeMutableRawPointer && pointerKind == PTK_UnsafeRawPointer) { if (type1PointerKind != pointerKind) increaseScore(ScoreKind::SK_ValueToPointerConversion, locator); conversionsOrFixes.push_back( ConversionRestrictionKind::PointerToPointer); } } // UnsafePointer and UnsafeRawPointer can also be converted from an // array or string value, or a UnsafePointer or // AutoreleasingUnsafeMutablePointer. if (pointerKind == PTK_UnsafePointer || pointerKind == PTK_UnsafeRawPointer) { if (!isAutoClosureArgument) { if (type1->isArray()) { conversionsOrFixes.push_back( ConversionRestrictionKind::ArrayToPointer); // If regular array-to-pointer conversion doesn't work, // let's try C pointer conversion that has special semantics // for imported declarations. if (isArgumentOfImportedDecl(locator)) { conversionsOrFixes.push_back( ConversionRestrictionKind::ArrayToCPointer); } } // The pointer can be converted from a string, if the element // type is compatible. auto &ctx = getASTContext(); if (type1->isString()) { auto baseTy = getFixedTypeRecursive(pointeeTy, false); if (baseTy->isTypeVariableOrMember() || isStringCompatiblePointerBaseType(ctx, baseTy)) conversionsOrFixes.push_back( ConversionRestrictionKind::StringToPointer); } } if (type1IsPointer && optionalityMatches && (type1PointerKind == PTK_UnsafePointer || type1PointerKind == PTK_AutoreleasingUnsafeMutablePointer)) { conversionsOrFixes.push_back( ConversionRestrictionKind::PointerToPointer); } } // If both sides are non-optional pointers, let's check whether // this argument supports Swift -> C pointer conversions. // // Do some light verification before recording restriction to // avoid allocating constraints for obviously invalid cases. if (type1IsPointer && !type1IsOptional && !type2IsOptional && (shouldAttemptFixes() || isArgumentOfImportedDecl(locator))) { // UnsafeRawPointer -> UnsafePointer<[U]Int8> if (type1PointerKind == PTK_UnsafeRawPointer && pointerKind == PTK_UnsafePointer) { conversionsOrFixes.push_back( ConversionRestrictionKind::PointerToCPointer); } // UnsafeMutableRawPointer -> Unsafe[Mutable]Pointer<[U]Int8> if (type1PointerKind == PTK_UnsafeMutableRawPointer && (pointerKind == PTK_UnsafePointer || pointerKind == PTK_UnsafeMutablePointer)) { conversionsOrFixes.push_back( ConversionRestrictionKind::PointerToCPointer); } // Unsafe[Mutable]Pointer -> Unsafe[Mutable]Pointer if (type1PointerKind == PTK_UnsafePointer && pointerKind == PTK_UnsafePointer) { conversionsOrFixes.push_back( ConversionRestrictionKind::PointerToCPointer); } if (type1PointerKind == PTK_UnsafeMutablePointer && (pointerKind == PTK_UnsafePointer || pointerKind == PTK_UnsafeMutablePointer)) { conversionsOrFixes.push_back( ConversionRestrictionKind::PointerToCPointer); } } } break; case PTK_AutoreleasingUnsafeMutablePointer: // PTK_AutoreleasingUnsafeMutablePointer can be converted from an // inout reference to a scalar. if (!isAutoClosureArgument && type1->is()) { conversionsOrFixes.push_back( ConversionRestrictionKind::InoutToPointer); } break; } } } } if (kind >= ConstraintKind::OperatorArgumentConversion) { // If the RHS is an inout type, the LHS must be an @lvalue type. if (auto *lvt = type1->getAs()) { if (auto *iot = type2->getAs()) { return matchTypes(lvt->getObjectType(), iot->getObjectType(), ConstraintKind::Bind, subflags, locator.withPathElement( ConstraintLocator::LValueConversion)); } } } // A value of type T! can be converted to type U if T is convertible // to U by force-unwrapping the source value. // A value of type T, T?, or T! can be converted to type U? or U! if // T is convertible to U. if (!type1->is() && kind >= ConstraintKind::Subtype) { enumerateOptionalConversionRestrictions( type1, type2, kind, locator, [&](ConversionRestrictionKind restriction) { conversionsOrFixes.push_back(restriction); }); } // Allow '() -> T' to '() -> ()' and '() -> Never' to '() -> T' for closure // literals and expressions representing an implied result of closures and // if/switch expressions. if (auto elt = locator.last()) { if (kind >= ConstraintKind::Subtype && (type1->isUninhabited() || type2->isVoid())) { // Implied results can occur for closure bodies, returns, and if/switch // expression branches. // // We only allow the Void conversion for implied results in a closure // context. In the more general case, we only allow the Never conversion. // For explicit branches, no conversions are allowed, unless this is for // a single expression body closure, in which case we still allow the // Never conversion. auto *loc = getConstraintLocator(locator); if (elt->is() || loc->isForContextualType(CTP_ReturnStmt) || loc->isForContextualType(CTP_ClosureResult) || loc->isForSingleValueStmtBranch()) { bool allowConversion = false; if (auto *E = getAsExpr(simplifyLocatorToAnchor(loc))) { if (auto kind = isImpliedResult(E)) { switch (*kind) { case ImpliedResultKind::Regular: allowConversion = type1->isUninhabited(); break; case ImpliedResultKind::ForClosure: allowConversion = true; break; } } else if (elt->is()) { // Even if explicit, we always allow uninhabited types in single // expression closures. allowConversion = type1->isUninhabited(); } } if (allowConversion) { increaseScore(SK_FunctionConversion, locator); return getTypeMatchSuccess(); } } } } // Matching types where one side is a pack expansion and the other is not // means a pack expansion was used where it isn't supported. if (type1->is() != type2->is()) { if (!shouldAttemptFixes()) return getTypeMatchFailure(locator); if (type1->isPlaceholder() || type2->isPlaceholder()) return getTypeMatchSuccess(); // If parameter pack expansion contains more than one element and the other // side is a tuple, record a fix. auto *loc = getConstraintLocator(locator); if (loc->isLastElement()) { if (auto packExpansion = type2->getAs()) { auto countType = simplifyType(packExpansion->getCountType(), flags); if (auto paramPack = countType->getAs()) { if (type1->is() && paramPack->getNumElements() >= 1) { if (recordFix(DestructureTupleToMatchPackExpansionParameter::create( *this, paramPack, loc))) { return getTypeMatchFailure(loc); } return getTypeMatchSuccess(); } } } } if (recordFix(AllowInvalidPackExpansion::create(*this, loc))) return getTypeMatchFailure(locator); return getTypeMatchSuccess(); } // Attempt fixes iff it's allowed, both types are concrete and // we are not in the middle of attempting one already. if (shouldAttemptFixes() && !flags.contains(TMF_ApplyingFix)) { if (repairFailures(type1, type2, kind, flags, conversionsOrFixes, locator)) { if (conversionsOrFixes.empty()) return getTypeMatchSuccess(); } } if (conversionsOrFixes.empty()) return getTypeMatchFailure(locator); // Where there is more than one potential conversion, create a disjunction // so that we'll explore all of the options. if (conversionsOrFixes.size() > 1) { auto fixedLocator = getConstraintLocator(locator); SmallVector constraints; for (auto potential : conversionsOrFixes) { auto constraintKind = kind; if (auto restriction = potential.getRestriction()) { // Determine the constraint kind. For a deep equality constraint, only // perform equality. if (*restriction == ConversionRestrictionKind::DeepEquality) constraintKind = ConstraintKind::Bind; constraints.push_back( Constraint::createRestricted(*this, constraintKind, *restriction, type1, type2, fixedLocator)); if (constraints.back()->getKind() == ConstraintKind::Bind) constraints.back()->setFavored(); continue; } auto fix = *potential.getFix(); constraints.push_back( Constraint::createFixed(*this, constraintKind, fix, type1, type2, fixedLocator)); } // Sort favored constraints first. std::sort(constraints.begin(), constraints.end(), [&](Constraint *lhs, Constraint *rhs) -> bool { if (lhs->isFavored() == rhs->isFavored()) return false; return lhs->isFavored(); }); addDisjunctionConstraint(constraints, fixedLocator); return getTypeMatchSuccess(); } // For a single potential conversion, directly recurse, so that we // don't allocate a new constraint or constraint locator. auto formTypeMatchResult = [&](SolutionKind kind) { switch (kind) { case SolutionKind::Error: return getTypeMatchFailure(locator); case SolutionKind::Solved: return getTypeMatchSuccess(); case SolutionKind::Unsolved: return getTypeMatchAmbiguous(); } llvm_unreachable("unhandled kind"); }; // Handle restrictions. if (auto restriction = conversionsOrFixes[0].getRestriction()) { return formTypeMatchResult(simplifyRestrictedConstraint(*restriction, type1, type2, kind, subflags, locator)); } // Handle fixes. auto fix = *conversionsOrFixes[0].getFix(); return formTypeMatchResult(simplifyFixConstraint(fix, type1, type2, kind, subflags, locator)); } ConstraintSystem::SolutionKind ConstraintSystem::simplifyConstructionConstraint( Type valueType, FunctionType *fnType, TypeMatchOptions flags, DeclContext *useDC, FunctionRefInfo functionRefInfo, ConstraintLocator *locator) { // Desugar the value type. auto desugarValueType = valueType->getDesugaredType(); switch (desugarValueType->getKind()) { #define SUGARED_TYPE(id, parent) case TypeKind::id: #define TYPE(id, parent) #include "swift/AST/TypeNodes.def" llvm_unreachable("Type has not been desugared completely"); #define ARTIFICIAL_TYPE(id, parent) case TypeKind::id: #define TYPE(id, parent) #include "swift/AST/TypeNodes.def" llvm_unreachable("artificial type in constraint"); case TypeKind::BuiltinTuple: llvm_unreachable("BuiltinTupleType in constraint"); case TypeKind::Unresolved: case TypeKind::Error: case TypeKind::Placeholder: return SolutionKind::Error; case TypeKind::GenericFunction: case TypeKind::GenericTypeParam: llvm_unreachable("unmapped dependent type"); case TypeKind::TypeVariable: case TypeKind::DependentMember: return SolutionKind::Unsolved; case TypeKind::Tuple: { // If this is an attempt to construct `Void` with arguments, // let's diagnose it. if (shouldAttemptFixes()) { if (valueType->isVoid() && fnType->getNumParams() > 0) { auto contextualType = FunctionType::get({}, fnType->getResult()); if (fixExtraneousArguments( *this, contextualType, fnType->getParams(), fnType->getNumParams(), getConstraintLocator(locator, ConstraintLocator::FunctionArgument))) return SolutionKind::Error; fnType = contextualType; } } SmallVector args; for (auto idx : indices(fnType->getParams())) { auto &arg = fnType->getParams()[idx]; // We can disregard '_const', it's not applicable for tuple construction. auto flags = arg.getParameterFlags().withCompileTimeLiteral(false); // We cannot handle inout for tuple construction. if (flags.isInOut()) { if (!shouldAttemptFixes()) return SolutionKind::Error; auto *argLoc = getConstraintLocator(locator, { LocatorPathElt::ApplyArgument(), LocatorPathElt::ApplyArgToParam(idx, idx, ParameterTypeFlags()) }); auto argTy = arg.getParameterType(); if (recordFix(RemoveAddressOf::create(*this, argTy, argTy, argLoc))) return SolutionKind::Error; flags = flags.withInOut(false); } args.push_back(arg.withFlags(flags)); } // Tuple construction is simply tuple conversion. We should have already // handled the parameter flags. If any future parameter flags are added, // they should also be verified above. Type argType = AnyFunctionType::composeTuple( getASTContext(), args, ParameterFlagHandling::AssertEmpty); Type resultType = fnType->getResult(); ConstraintLocatorBuilder builder(locator); if (matchTypes(resultType, desugarValueType, ConstraintKind::Bind, flags, builder.withPathElement(ConstraintLocator::ApplyFunction)) .isFailure()) return SolutionKind::Error; return matchTypes(argType, valueType, ConstraintKind::Conversion, getDefaultDecompositionOptions(flags), locator); } case TypeKind::Enum: case TypeKind::Struct: case TypeKind::Class: case TypeKind::BoundGenericClass: case TypeKind::BoundGenericEnum: case TypeKind::BoundGenericStruct: case TypeKind::PrimaryArchetype: case TypeKind::ExistentialArchetype: case TypeKind::OpaqueTypeArchetype: case TypeKind::PackArchetype: case TypeKind::ElementArchetype: case TypeKind::DynamicSelf: case TypeKind::ProtocolComposition: case TypeKind::ParameterizedProtocol: case TypeKind::Protocol: case TypeKind::Existential: case TypeKind::ErrorUnion: // Break out to handle the actual construction below. break; case TypeKind::UnboundGeneric: llvm_unreachable("Unbound generic type should have been opened"); #define BUILTIN_TYPE(id, parent) case TypeKind::id: #define TYPE(id, parent) #include "swift/AST/TypeNodes.def" case TypeKind::ExistentialMetatype: case TypeKind::Metatype: case TypeKind::Function: case TypeKind::LValue: case TypeKind::InOut: case TypeKind::Module: case TypeKind::Pack: case TypeKind::PackExpansion: case TypeKind::PackElement: { // If solver is in the diagnostic mode and this is an invalid base, // let's give solver a chance to repair it to produce a good diagnostic. if (shouldAttemptFixes()) break; return SolutionKind::Error; } case TypeKind::Integer: { llvm_unreachable("implement me"); } } auto fnLocator = getConstraintLocator(locator, ConstraintLocator::ApplyFunction); auto memberTypeLoc = getConstraintLocator(fnLocator, LocatorPathElt::ConstructorMemberType( /*shortFormOrSelfDelegating*/ true)); auto memberType = createTypeVariable(memberTypeLoc, TVO_CanBindToNoEscape); // The constructor will have function type T -> T2, for a fresh type // variable T. T2 is the result type provided via the construction // constraint itself. addValueMemberConstraint(MetatypeType::get(valueType, getASTContext()), DeclNameRef::createConstructor(), memberType, useDC, functionRefInfo, /*outerAlternatives=*/{}, getConstraintLocator( fnLocator, ConstraintLocator::ConstructorMember)); // HACK: Bind the function's parameter list as a tuple to a type variable. // This only exists to preserve compatibility with rdar://85263844, as it can // affect the prioritization of bindings, which can affect behavior for tuple // matching as tuple subtyping is currently a *weaker* constraint than tuple // conversion. if (!getASTContext().isSwiftVersionAtLeast(6)) { auto paramTypeVar = createTypeVariable( getConstraintLocator(locator, ConstraintLocator::ApplyArgument), TVO_CanBindToLValue | TVO_CanBindToInOut | TVO_CanBindToNoEscape | TVO_CanBindToPack); addConstraint(ConstraintKind::BindTupleOfFunctionParams, memberType, paramTypeVar, locator); } addApplicationConstraint(fnType, memberType, /*trailingClosureMatching=*/std::nullopt, useDC, fnLocator); return SolutionKind::Solved; } ConstraintSystem::SolutionKind ConstraintSystem::simplifySubclassOfConstraint( Type type, Type classType, ConstraintLocatorBuilder locator, TypeMatchOptions flags) { if (!classType->getClassOrBoundGenericClass()) return SolutionKind::Error; // Dig out the fixed type to which this type refers. type = getFixedTypeRecursive(type, flags, /*wantRValue=*/true); if (shouldAttemptFixes() && type->isPlaceholder()) { // If the type associated with this subclass check is a "hole" in the // constraint system, let's consider this check a success without recording // a fix, because it's just a consequence of the other failure, e.g. // // func foo(_: T) {} // foo(Foo.bar) <- if `Foo` doesn't have `bar` there is // no reason to complain the subclass. return SolutionKind::Solved; } auto formUnsolved = [&]() { // If we're supposed to generate constraints, do so. if (flags.contains(TMF_GenerateConstraints)) { auto *subclassOf = Constraint::create( *this, ConstraintKind::SubclassOf, type, classType, getConstraintLocator(locator)); addUnsolvedConstraint(subclassOf); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; // If we hit a type variable without a fixed type, we can't // solve this yet. if (type->isTypeVariableOrMember()) return formUnsolved(); // SubclassOf constraints are generated when opening a generic // signature with a RequirementKind::Superclass requirement, so // we must handle pack types on the left by splitting up into // smaller constraints. if (auto *packType = type->getAs()) { for (unsigned i = 0, e = packType->getNumElements(); i < e; ++i) { auto eltType = packType->getElementType(i); if (auto *packExpansionType = eltType->getAs()) { auto patternLoc = locator.withPathElement(ConstraintLocator::PackExpansionPattern); addConstraint(ConstraintKind::SubclassOf, packExpansionType->getPatternType(), classType, patternLoc); } else { addConstraint(ConstraintKind::SubclassOf, eltType, classType, locator.withPathElement(LocatorPathElt::PackElement(i))); } } return SolutionKind::Solved; } // A class-constrained existential like 'C & P' does not satisfy an // AnyObject requirement, if 'P' is not self-conforming. // // While matchSuperclassTypes() will still match here because 'C & P' // satisfies a Subtype constraint with 'C', 'C & P' cannot satisfy a // superclass requirement in a generic signature, so rule that out here. if (type->satisfiesClassConstraint()) { // If we have an exact match of class declarations, ensure the // generic arguments match. if (type->getClassOrBoundGenericClass() == classType->getClassOrBoundGenericClass()) { auto result = matchTypes(type, classType, ConstraintKind::Bind, flags, locator); if (!result.isFailure()) return SolutionKind::Solved; // Otherwise, ensure the left hand side is a proper subclass of the // right hand side. } else { auto result = matchSuperclassTypes(type, classType, flags, locator); if (!result.isFailure()) return SolutionKind::Solved; } } // Record a fix if we didn't match one of the two cases above. if (shouldAttemptFixes()) { if (auto *fix = fixRequirementFailure(*this, type, classType, locator)) { if (recordFix(fix)) return SolutionKind::Error; return SolutionKind::Solved; } } return SolutionKind::Error; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( Type type, Type protocol, ConstraintKind kind, ConstraintLocatorBuilder locator, TypeMatchOptions flags) { if (auto proto = protocol->getAs()) { return simplifyConformsToConstraint(type, proto->getDecl(), kind, locator, flags); } auto conformsToSubKind = (kind == ConstraintKind::NonisolatedConformsTo) ? kind : ConstraintKind::ConformsTo; // Dig out the fixed type to which this type refers. type = getFixedTypeRecursive(type, flags, /*wantRValue=*/true); // ConformsTo constraints are generated when opening a generic // signature with a RequirementKind::Conformance requirement, so // we must handle pack types on the left by splitting up into // smaller constraints. if (auto *packType = type->getAs()) { for (unsigned i = 0, e = packType->getNumElements(); i < e; ++i) { auto eltType = packType->getElementType(i); if (auto *packExpansionType = eltType->getAs()) { auto patternLoc = locator.withPathElement(ConstraintLocator::PackExpansionPattern); addConstraint(conformsToSubKind, packExpansionType->getPatternType(), protocol, patternLoc); } else { addConstraint(conformsToSubKind, eltType, protocol, locator.withPathElement(LocatorPathElt::PackElement(i))); } } return SolutionKind::Solved; } return matchExistentialTypes(type, protocol, kind, flags, locator); } void ConstraintSystem::recordSynthesizedConformance( ConstraintLocator *locator, ProtocolDecl *proto) { bool inserted = SynthesizedConformances.insert({locator, proto}).second; ASSERT(inserted); if (solverState) recordChange(SolverTrail::Change::RecordedSynthesizedConformance(locator)); } ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( Type type, ProtocolDecl *protocol, ConstraintKind kind, ConstraintLocatorBuilder locator, TypeMatchOptions flags) { const auto rawType = type; auto *typeVar = type->getAs(); // Dig out the fixed type to which this type refers. type = getFixedTypeRecursive(type, flags, /*wantRValue=*/true); if (shouldAttemptFixes() && type->isPlaceholder()) { // If the type associated with this conformance check is a "hole" in the // constraint system, let's consider this check a success without recording // a fix, because it's just a consequence of the other failure, e.g. // // func foo(_: T) {} // foo(Foo.bar) <- if `Foo` doesn't have `bar` there is // no reason to complain about missing conformance. return SolutionKind::Solved; } auto formUnsolved = [&](bool activate = false) { // If we're supposed to generate constraints, do so. if (flags.contains(TMF_GenerateConstraints)) { auto *conformance = Constraint::create( *this, kind, type, protocol->getDeclaredInterfaceType(), getConstraintLocator(locator)); addUnsolvedConstraint(conformance); if (activate) activateConstraint(conformance); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; // If we hit a type variable without a fixed type, we can't // solve this yet. if (type->isTypeVariableOrMember()) return formUnsolved(); auto conformsToSubKind = kind; if (kind != ConstraintKind::NonisolatedConformsTo) conformsToSubKind = ConstraintKind::ConformsTo; // ConformsTo constraints are generated when opening a generic // signature with a RequirementKind::Conformance requirement, so // we must handle pack types on the left by splitting up into // smaller constraints. if (auto *packType = type->getAs()) { for (unsigned i = 0, e = packType->getNumElements(); i < e; ++i) { auto eltType = packType->getElementType(i); if (auto *packExpansionType = eltType->getAs()) { auto patternLoc = locator.withPathElement(ConstraintLocator::PackExpansionPattern); addConstraint(conformsToSubKind, packExpansionType->getPatternType(), protocol->getDeclaredInterfaceType(), patternLoc); } else { addConstraint(conformsToSubKind, eltType, protocol->getDeclaredInterfaceType(), locator.withPathElement(LocatorPathElt::PackElement(i))); } } return SolutionKind::Solved; } // We sometimes get a pack expansion type here. if (auto *expansionType = type->getAs()) { addConstraint( conformsToSubKind, expansionType->getPatternType(), protocol->getDeclaredInterfaceType(), locator.withPathElement(LocatorPathElt::PackExpansionPattern())); return SolutionKind::Solved; } auto *loc = getConstraintLocator(locator); /// Record the given conformance as the result, adding any conditional /// requirements if necessary. auto recordConformance = [&](ProtocolConformanceRef conformance) { if (isConformanceUnavailable(conformance, loc)) increaseScore(SK_Unavailable, locator); unsigned numMissing = 0; conformance.forEachMissingConformance([&numMissing](auto *missing) { ++numMissing; return false; }); if (numMissing > 0) increaseScore(SK_MissingSynthesizableConformance, locator, numMissing); // If we aren't allowed to have an isolated conformance, check for any // isolated conformances here. if (kind == ConstraintKind::NonisolatedConformsTo && !conformance.getProtocol()->isMarkerProtocol()) { // Grab the first isolated conformance, if there is one. ProtocolConformanceRef isolatedConformance; conformance.forEachIsolatedConformance([&](ProtocolConformanceRef conf) { if (!isolatedConformance) isolatedConformance = conf; return true; }); if (isolatedConformance && isolatedConformance.isConcrete()) { auto fix = IgnoreIsolatedConformance::create( *this, getConstraintLocator(locator), isolatedConformance.getConcrete()); if (recordFix(fix)) { return SolutionKind::Error; } } } // This conformance may be conditional, in which case we need to consider // those requirements as constraints too. if (conformance.isConcrete()) { unsigned index = 0; auto *conformanceLoc = getConstraintLocator( loc, LocatorPathElt::ConformanceRequirement(conformance.getConcrete())); for (const auto &req : conformance.getConditionalRequirements()) { addConstraint( req, getConstraintLocator(conformanceLoc, LocatorPathElt::ConditionalRequirement( index++, req.getKind())), /*isFavored=*/false, kind == ConstraintKind::NonisolatedConformsTo); } } return SolutionKind::Solved; }; // For purposes of argument type matching, existential types don't need to // conform -- they only need to contain the protocol, so check that // separately. switch (kind) { case ConstraintKind::Subtype: { auto pair = TypeChecker::containsProtocol( type, protocol, /*allowMissing=*/true); if (pair.first) return SolutionKind::Solved; if (pair.second) return recordConformance(pair.second); } break; case ConstraintKind::NonisolatedConformsTo: case ConstraintKind::ConformsTo: case ConstraintKind::LiteralConformsTo: { // If existential type is used as a for-in sequence, let's open it // and check whether underlying type conforms to `Sequence`. if (type->isExistentialType()) { if (auto elt = loc->getLastElementAs()) { if (elt->getPurpose() == CTP_ForEachSequence) { type = openAnyExistentialType(type, loc).first; } } } // Check whether this type conforms to the protocol. auto conformance = lookupConformance(type, protocol); if (conformance) { return recordConformance(conformance); } // Account for ad-hoc requirements on some distributed actor // requirements. if (auto witnessInfo = locator.isForWitnessGenericParameterRequirement()) { auto *GP = witnessInfo->second; // Conformance requirement between on `Res` and `SerializationRequirement` // of `DistributedActorSystem.remoteCall` are not expressible at the moment // but they are verified by Sema so it's okay to omit them here and lookup // dynamically during IRGen. if (auto *witness = dyn_cast(witnessInfo->first)) { auto synthesizeConformance = [&]() { auto witnessLoc = getConstraintLocator( locator.getAnchor(), LocatorPathElt::Witness(witness)); // FIXME: Why are we recording the same locator more than once here? if (SynthesizedConformances.count(witnessLoc) == 0) recordSynthesizedConformance(witnessLoc, protocol); return SolutionKind::Solved; }; if (witness->isGeneric()) { // `DistributedActorSystem.remoteCall` if (witness->isDistributedActorSystemRemoteCall(/*isVoidReturn=*/false)) { if (GP->isEqual(cast(witness)->getResultInterfaceType())) return synthesizeConformance(); } // `DistributedTargetInvocationEncoder.record{Argument, ResultType}` // `DistributedTargetInvocationDecoder.decodeNextArgument` // `DistributedTargetInvocationResultHandler.onReturn` if (witness->isDistributedTargetInvocationEncoderRecordArgument() || witness->isDistributedTargetInvocationEncoderRecordReturnType() || witness ->isDistributedTargetInvocationDecoderDecodeNextArgument() || witness->isDistributedTargetInvocationResultHandlerOnReturn()) { auto genericParams = witness->getGenericParams()->getParams(); if (GP->isEqual(genericParams.front()->getDeclaredInterfaceType())) return synthesizeConformance(); } } } } auto arrayLiteralProto = getASTContext().getProtocol(KnownProtocolKind::ExpressibleByArrayLiteral); auto anchor = loc->getAnchor(); auto arrayLiteral = getAsExpr(anchor); // If we're attempting to bind an array literal to an 'InlineArray' // parameter, then check if the counts are equal and solve. if (kind == ConstraintKind::LiteralConformsTo && protocol == arrayLiteralProto && type->isInlineArray() && arrayLiteral) { auto iaTy = type->castTo(); // // Attempt to bind the number of elements in the literal with the // contextual count. This will diagnose if the literal does not enough // or too many elements. auto contextualCount = iaTy->getGenericArgs()[0]; auto literalCount = IntegerType::get( std::to_string(arrayLiteral->getNumElements()), /* isNegative */ false, iaTy->getASTContext()); // If the counts are already equal, '2' == '2', then we're done. if (contextualCount->isEqual(literalCount)) { return SolutionKind::Solved; } // If our contextual count is not known, e.g., InlineArray<_, Int> = [1, 2], // then just eagerly bind the count to what the literal count is. if (contextualCount->isTypeVariableOrMember()) { addConstraint(ConstraintKind::Bind, contextualCount, literalCount, locator); return SolutionKind::Solved; } // Otherwise this is an error and the counts aren't equal to each other. if (!shouldAttemptFixes()) return SolutionKind::Error; auto fix = AllowInlineArrayLiteralCountMismatch::create(*this, contextualCount, literalCount, loc); return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } } break; default: llvm_unreachable("bad constraint kind"); } if (!shouldAttemptFixes()) return SolutionKind::Error; auto protocolTy = protocol->getDeclaredInterfaceType(); // If this conformance has been fixed already, let's just consider this done. if (isFixedRequirement(loc, protocolTy)) return SolutionKind::Solved; // If this is a generic requirement let's try to record that // conformance is missing and consider this a success, which // makes it much easier to diagnose problems like that. { SmallVector path; auto anchor = locator.getLocatorParts(path); // If this is a `nil` literal, it would be a contextual failure. if (auto *Nil = getAsExpr(anchor)) { auto *fixLocator = getConstraintLocator( getContextualType(Nil, /*forConstraint=*/false) ? locator.withPathElement(LocatorPathElt::ContextualType( getContextualTypePurpose(Nil))) : locator); // Only requirement placed directly on `nil` literal is // `ExpressibleByNilLiteral`, so if `nil` is an argument // to an application, let's update locator accordingly to // diagnose possible ambiguities with multiple mismatched // overload choices. if (fixLocator->directlyAt()) { if (auto *loc = getArgumentLocator(castToExpr(fixLocator->getAnchor()))) fixLocator = loc; } // Here the roles are reversed - `nil` is something we are trying to // convert to `type` by making sure that it conforms to a specific // protocol. auto *fix = ContextualMismatch::create(*this, protocolTy, type, fixLocator); return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } // If there is a missing conformance between source and destination // of the assignment, let's ignore current the types and instead use // source/destination types directly to make it possible to diagnose // protocol compositions. if (auto *assignment = getAsExpr(anchor)) { // If the locator's last element points to the function result, // let's check whether there is a problem with function argument // as well, and if so, avoid producing a fix here, because // contextual mismatch mentions the source/destination // types of the assignment. if (locator.endsWith() && hasFixFor( getConstraintLocator(anchor, LocatorPathElt::FunctionArgument()))) return SolutionKind::Solved; auto srcType = getType(assignment->getSrc()); auto dstType = getType(assignment->getDest()); auto *fix = IgnoreAssignmentDestinationType::create( *this, srcType, dstType, loc); return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } if (path.empty()) return SolutionKind::Error; // If this is a conformance failure related to a contextual type // let's record it as a "contextual mismatch" because diagnostic // is going to be dependent on other contextual information. if (path.back().is()) { auto *fix = ContextualMismatch::create(*this, type, protocolTy, loc); return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } // Conditional conformance requirements could produce chains of // `path element -> pack expansion pattern -> pack element`. while (!path.empty()) { // If we have something like ... -> type req # -> pack element #, we're // solving a requirement of the form T : P where T is a type parameter pack if (path.back().is()) { path.pop_back(); continue; } // This is similar to `PackElement` but locator points to the requirement // associated with pack expansion pattern (i.e. `repeat each T: P`) where // the path is something like: // `... -> type req # -> pack expansion pattern`. if (path.back().is()) { path.pop_back(); continue; } break; } if (auto req = path.back().getAs()) { // If this is a requirement associated with `Self` which is bound // to `Any`, let's consider this "too incorrect" to continue. // // This helps us to filter out cases like operator overloads where // `Self` type comes from e.g. default for collection element - // `[1, "hello"].map { $0 + 1 }`. Main problem here is that // collection type couldn't be determined without unification to // `Any` and `+` failing for all numeric overloads is just a consequence. if (typeVar && type->isAny()) { if (auto *GP = typeVar->getImpl().getGenericParameter()) { if (auto *GPD = GP->getDecl()) { auto *DC = GPD->getDeclContext(); if (DC->isTypeContext() && DC->getSelfInterfaceType()->isEqual(GP)) return SolutionKind::Error; } } } if (auto rawValue = isRawRepresentable(*this, type)) { if (!rawValue->isTypeVariableOrMember() && lookupConformance(rawValue, protocol)) { auto *fix = UseRawValue::create(*this, type, protocolTy, loc); // Since this is a conformance requirement failure (where the // source is most likely an argument), let's increase its impact // to disambiguate vs. conversion failure of the same kind. return recordFix(fix, /*impact=*/2) ? SolutionKind::Error : SolutionKind::Solved; } } auto anchor = locator.getAnchor(); if (isExpr(anchor) && req->is()) { auto signature = path[path.size() - 2] .castTo() .getSignature(); auto requirement = signature.getRequirements()[req->getIndex()]; auto *memberLoc = getConstraintLocator(anchor, path.front()); auto overload = findSelectedOverloadFor(memberLoc); // To figure out what is going on here we need to wait until // member overload is set in the constraint system. if (!overload) { // If it's not allowed to generate new constraints // there is no way to control re-activation, so this // check has to fail. if (!flags.contains(TMF_GenerateConstraints)) return SolutionKind::Error; return formUnsolved(/*activate=*/true); } auto *memberRef = overload->choice.getDeclOrNull(); if (!memberRef) return SolutionKind::Error; // If this is a `Self` conformance requirement from a static member // reference on a protocol metatype, let's produce a tailored diagnostic. if (memberRef->isStatic()) { if (hasFixFor(memberLoc, FixKind::AllowInvalidStaticMemberRefOnProtocolMetatype)) return SolutionKind::Solved; if (auto *protocolDecl = memberRef->getDeclContext()->getSelfProtocolDecl()) { auto selfTy = protocolDecl->getSelfInterfaceType(); if (selfTy->isEqual(requirement.getFirstType())) { auto *fix = AllowInvalidStaticMemberRefOnProtocolMetatype::create( *this, memberLoc); return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } } } } if (auto *fix = fixRequirementFailure(*this, type, protocolTy, anchor, path)) { auto impact = assessRequirementFailureImpact(*this, rawType, locator); if (!recordFix(fix, impact)) { // Record this conformance requirement as "fixed". recordFixedRequirement(getConstraintLocator(anchor, path), protocolTy); return SolutionKind::Solved; } } } if (loc->isLastElement()) { auto *fix = ContextualMismatch::create(*this, protocolTy, type, loc); if (!recordFix(fix)) return SolutionKind::Solved; } // Conformance constraint that is introduced by an implicit conversion // for example to `AnyHashable`. if ((kind == ConstraintKind::ConformsTo || kind == ConstraintKind::NonisolatedConformsTo) && loc->isLastElement()) { auto *fix = AllowArgumentMismatch::create(*this, type, protocolTy, loc); return recordFix(fix, /*impact=*/2) ? SolutionKind::Error : SolutionKind::Solved; } // If this is an implicit Hashable conformance check generated for each // index argument of the keypath subscript component, we could just treat // it as though it conforms. if ((loc->isResultOfKeyPathDynamicMemberLookup() || loc->isKeyPathSubscriptComponent()) || loc->isKeyPathMemberComponent()) { if (protocol == getASTContext().getProtocol(KnownProtocolKind::Hashable)) { auto *fix = TreatKeyPathSubscriptIndexAsHashable::create(*this, type, loc); if (!recordFix(fix)) return SolutionKind::Solved; } } } // There's nothing more we can do; fail. return SolutionKind::Error; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyTransitivelyConformsTo( Type type, Type protocolTy, ConstraintLocatorBuilder locator, TypeMatchOptions flags) { auto &ctx = getASTContext(); // Since this is a performance optimization, let's ignore it // in diagnostic mode. if (shouldAttemptFixes()) return SolutionKind::Solved; auto formUnsolved = [&]() { // If we're supposed to generate constraints, do so. if (flags.contains(TMF_GenerateConstraints)) { auto *conformance = Constraint::create(*this, ConstraintKind::TransitivelyConformsTo, type, protocolTy, getConstraintLocator(locator)); addUnsolvedConstraint(conformance); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; auto resolvedTy = getFixedTypeRecursive(type, /*wantRValue=*/true); if (resolvedTy->isTypeVariableOrMember()) return formUnsolved(); // If the composition consists of a class + protocol, // we can't check conformance of the argument because // parameter could pick one of the components. if (resolvedTy.findIf( [](Type type) { return type->is(); })) return SolutionKind::Solved; // All bets are off for pointers, there are multiple combinations // to check and it doesn't see worth to do that upfront. { PointerTypeKind pointerKind; if (resolvedTy->getAnyPointerElementType(pointerKind)) return SolutionKind::Solved; } auto *protocol = protocolTy->castTo()->getDecl(); // First, let's check whether the type itself conforms, // if it does - we are done. if (lookupConformance(resolvedTy, protocol)) return SolutionKind::Solved; // If the type doesn't conform, let's check whether // an Optional or Unsafe{Mutable}Pointer from it would. // If the current score is equal to the best score, fail without checking // implicit conversions, because an implicit conversion would lead to a // worse score anyway. if (solverState && solverState->BestScore && CurrentScore == *solverState->BestScore) return SolutionKind::Error; SmallVector typesToCheck; // T -> Optional if (!resolvedTy->getOptionalObjectType()) typesToCheck.push_back(OptionalType::get(resolvedTy)); // AnyHashable if (auto *anyHashable = ctx.getAnyHashableDecl()) typesToCheck.push_back(anyHashable->getDeclaredInterfaceType()); // Rest of the implicit conversions depend on the resolved type. { auto getPointerFor = [&ctx](PointerTypeKind ptrKind, std::optional elementTy = std::nullopt) -> Type { switch (ptrKind) { case PTK_UnsafePointer: assert(elementTy); return BoundGenericType::get(ctx.getUnsafePointerDecl(), /*parent=*/Type(), {*elementTy}); case PTK_UnsafeMutablePointer: assert(elementTy); return BoundGenericType::get(ctx.getUnsafeMutablePointerDecl(), /*parent=*/Type(), {*elementTy}); case PTK_UnsafeRawPointer: return ctx.getUnsafeRawPointerDecl()->getDeclaredInterfaceType(); case PTK_UnsafeMutableRawPointer: return ctx.getUnsafeMutableRawPointerDecl()->getDeclaredInterfaceType(); case PTK_AutoreleasingUnsafeMutablePointer: llvm_unreachable("no implicit conversion"); } }; // String -> UnsafePointer if (auto *string = ctx.getStringDecl()) { if (resolvedTy->isEqual(string->getDeclaredInterfaceType())) { typesToCheck.push_back( getPointerFor(PTK_UnsafePointer, ctx.TheEmptyTupleType)); } } // Array -> Unsafe{Raw}Pointer if (auto elt = resolvedTy->getArrayElementType()) { typesToCheck.push_back(getPointerFor(PTK_UnsafePointer, elt)); typesToCheck.push_back(getPointerFor(PTK_UnsafeRawPointer, elt)); } // inout argument -> UnsafePointer, UnsafeMutablePointer, // UnsafeRawPointer, UnsafeMutableRawPointer. if (type->is()) { typesToCheck.push_back(getPointerFor(PTK_UnsafePointer, resolvedTy)); typesToCheck.push_back(getPointerFor(PTK_UnsafeMutablePointer, resolvedTy)); typesToCheck.push_back(getPointerFor(PTK_UnsafeRawPointer)); typesToCheck.push_back(getPointerFor(PTK_UnsafeMutableRawPointer)); } } return llvm::any_of( typesToCheck, [&](Type type) { return bool(lookupConformance(type, protocol)); }) ? SolutionKind::Solved : SolutionKind::Error; } /// Determine the kind of checked cast to perform from the given type to /// the given type. /// /// This routine does not attempt to check whether the cast can actually /// succeed; that's the caller's responsibility. static CheckedCastKind getCheckedCastKind(ConstraintSystem *cs, Type fromType, Type toType) { // Array downcasts are handled specially. if (fromType->isArray() && toType->isArray()) { return CheckedCastKind::ArrayDowncast; } // Dictionary downcasts are handled specially. if (cs->isDictionaryType(fromType) && cs->isDictionaryType(toType)) { return CheckedCastKind::DictionaryDowncast; } // Set downcasts are handled specially. if (cs->isSetType(fromType) && cs->isSetType(toType)) { return CheckedCastKind::SetDowncast; } return CheckedCastKind::ValueCast; } // Optional types always conform to `ExpressibleByNilLiteral`. static bool isCastToExpressibleByNilLiteral(ConstraintSystem &cs, Type fromType, Type toType) { auto &ctx = cs.getASTContext(); auto *nilLiteral = ctx.getProtocol(KnownProtocolKind::ExpressibleByNilLiteral); if (!nilLiteral) return false; return toType->isEqual(nilLiteral->getDeclaredExistentialType()) && fromType->getOptionalObjectType(); } static ConstraintFix *maybeWarnAboutExtraneousCast( ConstraintSystem &cs, Type origFromType, Type origToType, Type fromType, Type toType, SmallVector fromOptionals, SmallVector toOptionals, ConstraintSystem::TypeMatchOptions flags, ConstraintLocatorBuilder locator) { if (locator.endsWith()) return nullptr; // Both types have to be resolved, `typeCheckCheckedCast` doesn't support // checking solver-allocated types. if (fromType->hasTypeVariableOrPlaceholder() || toType->hasTypeVariableOrPlaceholder()) { return nullptr; } SmallVector path; auto anchor = locator.getLocatorParts(path); auto *castExpr = getAsExpr(anchor); if (!castExpr) return nullptr; // "from" could be less optional than "to" e.g. `0 as Any?`, so // we need to store the difference as a signed integer. int extraOptionals = fromOptionals.size() - toOptionals.size(); // "from" expression could be a type variable wrapped in an optional e.g. // Optional<$T0>. So when that is the case we have to add this additional // optionality levels to from type. const auto subExprType = cs.getType(castExpr->getSubExpr()); if (subExprType->getOptionalObjectType()) { SmallVector subExprOptionals; const auto unwrappedSubExprType = subExprType->lookThroughAllOptionalTypes(subExprOptionals); if (unwrappedSubExprType->is()) { extraOptionals += subExprOptionals.size(); for (size_t i = 0; i != subExprOptionals.size(); ++i) { origFromType = OptionalType::get(origFromType); } } } // Removing the optionality from to type when the force cast expr is an IUO. const auto *const TR = castExpr->getCastTypeRepr(); if (isExpr(anchor) && TR && TR->getKind() == TypeReprKind::ImplicitlyUnwrappedOptional) { extraOptionals++; } // In cases of 'try?' where origFromType isn't optional that meaning // sub-expression isn't optional, always adds one level of optionality // because the result of the expression is always an optional type // regardless of language mode. auto *sub = castExpr->getSubExpr()->getSemanticsProvidingExpr(); if (isExpr(sub) && !origFromType->getOptionalObjectType()) { origFromType = OptionalType::get(fromType); extraOptionals++; } // Except for forced cast expressions, if optionals are more than a single // level difference or there is a single level between the types but an extra // level of optional is added to subexpr via OptionalEvaluationExpr, we don't // need to record any fix. if (!isExpr(anchor) && (extraOptionals > 1 || isExpr(castExpr->getSubExpr()))) return nullptr; // Always succeed if (isCastToExpressibleByNilLiteral(cs, origFromType, toType)) { return AllowNoopCheckedCast::create(cs, fromType, toType, CheckedCastKind::Coercion, cs.getConstraintLocator(locator)); } // If both original are metatypes we have to use them because most of the // logic on how correctly handle metatypes casting is on // typeCheckCheckedCast. if (origFromType->is() && origToType->is()) { fromType = origFromType; toType = origToType; } auto castKind = TypeChecker::typeCheckCheckedCast( fromType, toType, CheckedCastContextKind::None, cs.DC); if (castKind == CheckedCastKind::Unresolved) { return AllowCheckedCastToUnrelated::attempt( cs, origFromType, origToType, castKind, cs.getConstraintLocator(locator)); } if (castKind == CheckedCastKind::ValueCast) { // https://github.com/apple/swift/issues/44221 // Special 'is' case diagnostics for CFTypes. return AllowNoopExistentialToCFTypeCheckedCast::attempt( cs, origFromType, origToType, castKind, cs.getConstraintLocator(locator)); } if (!(castKind == CheckedCastKind::Coercion || castKind == CheckedCastKind::BridgingCoercion)) return nullptr; if (auto *fix = AllowUnsupportedRuntimeCheckedCast::attempt( cs, fromType, toType, castKind, cs.getConstraintLocator(locator))) { return fix; } if (extraOptionals > 0) { // Conditional cast in this case can be an attempt to just unwrap a nil // value. if (isExpr(anchor) && castKind == CheckedCastKind::BridgingCoercion) { return nullptr; } return AllowCheckedCastCoercibleOptionalType::create( cs, origFromType, origToType, castKind, cs.getConstraintLocator(locator)); } else { // No optionals, just a trivial cast that always succeeds. return AllowNoopCheckedCast::create(cs, origFromType, origToType, castKind, cs.getConstraintLocator(locator)); } } ConstraintSystem::SolutionKind ConstraintSystem::simplifyCheckedCastConstraint( Type fromType, Type toType, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); /// Form an unresolved result. auto formUnsolved = [&] { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( Constraint::create(*this, ConstraintKind::CheckedCast, fromType, toType, getConstraintLocator(locator))); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; Type origFromType = getFixedTypeRecursive(fromType, flags, /*wantRValue=*/true); Type origToType = getFixedTypeRecursive(toType, flags, /*wantRValue=*/true); SmallVector fromOptionals; SmallVector toOptionals; do { // Dig out the fixed type this type refers to. fromType = getFixedTypeRecursive(fromType, flags, /*wantRValue=*/true); // If we hit a type variable without a fixed type, we can't // solve this yet. if (fromType->isTypeVariableOrMember()) return formUnsolved(); // Dig out the fixed type this type refers to. toType = getFixedTypeRecursive(toType, flags, /*wantRValue=*/true); // If we hit a type variable without a fixed type, we can't // solve this yet. if (toType->isTypeVariableOrMember()) return formUnsolved(); Type origFromType = fromType; Type origToType = toType; // Peel off optionals metatypes from the types, because we might cast through // them. toType = toType->lookThroughAllOptionalTypes(toOptionals); fromType = fromType->lookThroughAllOptionalTypes(fromOptionals); // Peel off metatypes, since if we can cast two types, we can cast their // metatypes. while (auto toMetatype = toType->getAs()) { auto fromMetatype = fromType->getAs(); if (!fromMetatype) break; toType = toMetatype->getInstanceType(); fromType = fromMetatype->getInstanceType(); } // Peel off a potential layer of existential<->concrete metatype conversion. if (auto toMetatype = toType->getAs()) { if (auto fromMetatype = fromType->getAs()) { toType = toMetatype->getInstanceType(); fromType = fromMetatype->getInstanceType(); } } // Peel off marker protocol requirements if this is an existential->concrete // cast. Handles cases like `WritableKeyPath<...> & Sendable as KeyPath` // that require inference which is only attempted if both sides are classes. if (fromType->isExistentialType() && !toType->isExistentialType()) { if (auto *existential = fromType->getAs()) { if (auto *PCT = existential->getConstraintType() ->getAs()) { auto newConstraintTy = PCT->withoutMarkerProtocols(); if (!newConstraintTy->isEqual(PCT)) { fromType = newConstraintTy->getClassOrBoundGenericClass() ? newConstraintTy : ExistentialType::get(newConstraintTy); } } } } // We've decomposed the types further, so adopt the subflags. flags = subflags; // If nothing changed, we're done. if (fromType.getPointer() == origFromType.getPointer() && toType.getPointer() == origToType.getPointer()) break; } while (true); auto attemptRecordCastFixIfSolved = [&](SolutionKind result) { if (result != SolutionKind::Solved) return; if (auto *fix = maybeWarnAboutExtraneousCast( *this, origFromType, origToType, fromType, toType, fromOptionals, toOptionals, flags, locator)) { (void)recordFix(fix); } }; auto kind = getCheckedCastKind(this, fromType, toType); switch (kind) { case CheckedCastKind::ArrayDowncast: { auto fromBaseType = fromType->getArrayElementType(); auto toBaseType = toType->getArrayElementType(); auto elementLocator = locator.withPathElement(LocatorPathElt::GenericArgument(0)); auto result = simplifyCheckedCastConstraint(fromBaseType, toBaseType, subflags, elementLocator); attemptRecordCastFixIfSolved(result); return result; } case CheckedCastKind::DictionaryDowncast: { Type fromKeyType, fromValueType; std::tie(fromKeyType, fromValueType) = *isDictionaryType(fromType); Type toKeyType, toValueType; std::tie(toKeyType, toValueType) = *isDictionaryType(toType); auto keyLocator = locator.withPathElement(LocatorPathElt::GenericArgument(0)); if (simplifyCheckedCastConstraint(fromKeyType, toKeyType, subflags, keyLocator) == SolutionKind::Error) return SolutionKind::Error; auto valueLocator = locator.withPathElement(LocatorPathElt::GenericArgument(1)); auto result = simplifyCheckedCastConstraint(fromValueType, toValueType, subflags, valueLocator); attemptRecordCastFixIfSolved(result); return result; } case CheckedCastKind::SetDowncast: { auto fromBaseType = *isSetType(fromType); auto toBaseType = *isSetType(toType); auto elementLocator = locator.withPathElement(LocatorPathElt::GenericArgument(0)); auto result = simplifyCheckedCastConstraint(fromBaseType, toBaseType, subflags, elementLocator); attemptRecordCastFixIfSolved(result); return result; } case CheckedCastKind::ValueCast: { // If casting among classes, and there are open // type variables remaining, introduce a subtype constraint to help resolve // them. if (fromType->getClassOrBoundGenericClass() && toType->getClassOrBoundGenericClass() && (fromType->hasTypeVariable() || toType->hasTypeVariable())) { addConstraint(ConstraintKind::Subtype, toType, fromType, getConstraintLocator(locator)); } // Attempts to record warning fixes when both types are known by the // compiler and we can infer that the runtime checked cast will always // succeed or fail. if (auto *fix = maybeWarnAboutExtraneousCast( *this, origFromType, origToType, fromType, toType, fromOptionals, toOptionals, flags, locator)) { (void)recordFix(fix); } return SolutionKind::Solved; } case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: case CheckedCastKind::Unresolved: llvm_unreachable("Not a valid result"); } llvm_unreachable("Unhandled CheckedCastKind in switch."); } ConstraintSystem::SolutionKind ConstraintSystem::simplifyOptionalObjectConstraint( Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { // Resolve the optional type. Type optLValueTy = getFixedTypeRecursive(first, flags, /*wantRValue=*/false); Type optTy = optLValueTy->getRValueType(); if (optTy.getPointer() != optLValueTy.getPointer()) optTy = getFixedTypeRecursive(optTy, /*wantRValue=*/false); if (optTy->isTypeVariableOrMember()) { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( Constraint::create(*this, ConstraintKind::OptionalObject, optLValueTy, second, getConstraintLocator(locator))); return SolutionKind::Solved; } return SolutionKind::Unsolved; } if (optTy->isPlaceholder()) { if (auto *typeVar = second->getAs()) recordPotentialHole(typeVar); return SolutionKind::Solved; } Type objectTy = optTy->getOptionalObjectType(); // If the base type is not optional, let's attempt a fix (if possible) // and assume that `!` is just not there. if (!objectTy) { if (!shouldAttemptFixes()) return SolutionKind::Error; // Let's see if we can apply a specific fix here. if (optTy->isPlaceholder()) return SolutionKind::Solved; auto fnType = optTy->getAs(); if (fnType && fnType->getNumParams() == 0) { // For function types with no parameters, let's try to // offer a "make it a call" fix if possible. auto optionalResultType = fnType->getResult()->getOptionalObjectType(); if (optionalResultType) { if (matchTypes(optionalResultType, second, ConstraintKind::Bind, flags | TMF_ApplyingFix, locator) .isSuccess()) { auto *fix = InsertExplicitCall::create(*this, getConstraintLocator(locator)); return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } } } auto *fix = RemoveUnwrap::create(*this, optTy, getConstraintLocator(locator)); if (recordFix(fix)) return SolutionKind::Error; // If the fix was successful let's record // "fixed" object type and continue. objectTy = optTy; } // The object type is an lvalue if the optional was. if (optLValueTy->is()) objectTy = LValueType::get(objectTy); // Equate it to the other type in the constraint. addConstraint(ConstraintKind::Bind, objectTy, second, locator); return SolutionKind::Solved; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyBindTupleOfFunctionParamsConstraint( Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto simplified = simplifyType(first); auto simplifiedCopy = simplified; unsigned unwrapCount = 0; if (shouldAttemptFixes()) { while (auto objectTy = simplified->getOptionalObjectType()) { simplified = objectTy; // Track how many times we do this so that we can record a fix for each. ++unwrapCount; } if (simplified->isPlaceholder()) { if (auto *typeVar = second->getAs()) recordPotentialHole(typeVar); return SolutionKind::Solved; } } if (simplified->isTypeVariableOrMember()) { if (!flags.contains(TMF_GenerateConstraints)) return SolutionKind::Unsolved; addUnsolvedConstraint( Constraint::create(*this, ConstraintKind::BindTupleOfFunctionParams, simplified, second, getConstraintLocator(locator))); return SolutionKind::Solved; } auto *funcTy = simplified->getAs(); if (!funcTy) return SolutionKind::Error; auto tupleTy = AnyFunctionType::composeTuple(getASTContext(), funcTy->getParams(), ParameterFlagHandling::IgnoreNonEmpty); addConstraint(ConstraintKind::Bind, tupleTy, second, locator.withPathElement(ConstraintLocator::FunctionArgument)); if (unwrapCount > 0) { auto *fix = ForceOptional::create(*this, simplifiedCopy, second, getConstraintLocator(locator)); if (recordFix(fix, /*impact=*/unwrapCount)) return SolutionKind::Error; } return SolutionKind::Solved; } ConstraintSystem::SolutionKind ConstraintSystem::matchPackElementType(Type elementType, Type patternType, ConstraintLocatorBuilder locator) { auto tryFix = [&](llvm::function_ref fix) { if (!shouldAttemptFixes()) return SolutionKind::Error; if (recordFix(fix())) return SolutionKind::Error; recordAnyTypeVarAsPotentialHole(elementType); return SolutionKind::Solved; }; auto *loc = getConstraintLocator(locator); ASSERT(loc->directlyAt()); auto *packExpansion = castToExpr(loc->getAnchor()); ASSERT(!patternType->hasTypeVariable()); auto shapeClass = patternType->getReducedShape(); // `each` was applied to a concrete type. if (!shapeClass->is()) { return tryFix([&]() { return AllowInvalidPackElement::create(*this, patternType, loc); }); } auto shapeParam = CanGenericTypeParamType(cast( shapeClass->mapTypeOutOfContext()->getCanonicalType())); auto *genericEnv = getPackExpansionEnvironment(packExpansion); if (genericEnv) { if (shapeParam != genericEnv->getOpenedElementShapeClass()) { return tryFix([&]() { auto envShape = genericEnv->mapTypeIntoContext( genericEnv->getOpenedElementShapeClass()); if (auto *pack = dyn_cast(envShape)) envShape = pack->unwrapSingletonPackExpansion()->getPatternType(); return SkipSameShapeRequirement::create( *this, envShape, shapeClass, getConstraintLocator(loc, ConstraintLocator::PackShape)); }); } } else { genericEnv = createPackExpansionEnvironment(packExpansion, shapeParam); } auto expectedElementTy = genericEnv->mapContextualPackTypeIntoElementContext(patternType); assert(!expectedElementTy->is()); addConstraint(ConstraintKind::Equal, elementType, expectedElementTy, locator); return SolutionKind::Solved; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyPackElementOfConstraint(Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto elementType = simplifyType(first, flags); auto patternType = simplifyType(second, flags); auto formUnsolved = [&]() { if (!flags.contains(TMF_GenerateConstraints)) return SolutionKind::Unsolved; addUnsolvedConstraint( Constraint::create(*this, ConstraintKind::PackElementOf, first, second, getConstraintLocator(locator))); return SolutionKind::Solved; }; // If neither side is fully resolved yet, there is nothing we can do. if (elementType->hasTypeVariable() && patternType->hasTypeVariable()) return formUnsolved(); if (shouldAttemptFixes()) { if (elementType->isPlaceholder() || patternType->isPlaceholder()) return SolutionKind::Solved; } if (isSingleUnlabeledPackExpansionTuple(patternType)) { auto *packVar = addMaterializePackExpansionConstraint(patternType, locator); addConstraint(ConstraintKind::PackElementOf, elementType, packVar, locator); return SolutionKind::Solved; } // Let's try to resolve element type based on the pattern type. if (!patternType->hasTypeVariable()) return matchPackElementType(elementType, patternType, locator); // Otherwise we are inferred or checking pattern type. auto *packEnv = DC->getGenericEnvironmentOfContext(); // Map element archetypes to the pack context to check for equality. if (elementType->hasElementArchetype()) elementType = packEnv->mapElementTypeIntoPackContext(elementType); addConstraint(ConstraintKind::Equal, elementType, patternType, locator); return SolutionKind::Solved; } static bool isForKeyPathSubscript(ConstraintSystem &cs, ConstraintLocator *locator) { if (!locator || !locator->getAnchor()) return false; if (auto *SE = getAsExpr(locator->getAnchor())) { return SE->getArgs()->isUnary() && SE->getArgs()->getLabel(0) == cs.getASTContext().Id_keyPath; } return false; } static bool mayBeForKeyPathSubscriptWithoutLabel(ConstraintSystem &cs, ConstraintLocator *locator) { if (!locator || !locator->getAnchor()) return false; if (auto *SE = getAsExpr(locator->getAnchor())) { if (auto *unary = SE->getArgs()->getUnlabeledUnaryExpr()) return isa(unary) || isa(unary); } return false; } /// Determine whether all of the given candidate overloads /// found through conditional conformances of a given base type. /// This is useful to figure out whether it makes sense to /// perform dynamic member lookup or not. static bool allFromConditionalConformances(ConstraintSystem &cs, Type baseTy, ArrayRef candidates) { auto *NTD = baseTy->getAnyNominal(); if (!NTD) return false; return llvm::all_of(candidates, [&](const OverloadChoice &choice) { auto *decl = choice.getDeclOrNull(); if (!decl) return false; auto *candidateDC = decl->getDeclContext(); if (auto *extension = dyn_cast(candidateDC)) { if (extension->isConstrainedExtension()) return true; } if (auto *protocol = candidateDC->getSelfProtocolDecl()) { auto conformance = cs.lookupConformance(baseTy, protocol); if (!conformance.isConcrete()) return false; return !conformance.getConcrete()->getConditionalRequirements().empty(); } return false; }); } // Check whether given key path dynamic member lookup is self-recursive, // which happens when root type of the key path is the same as base type // of the member and lookup is attempted on non-existing property e.g. // // @dynamicMemberLookup // struct Recurse { // subscript(dynamicMember member: KeyPath, U>) -> Int { // return 1 // } // } // // If we going to lookup any no-existent property or member on `Recursive` // using key path dynamic member lookup it would attempt to lookup such // member on root type which is also `Recursive` which leads to an infinite // recursion. static bool isSelfRecursiveKeyPathDynamicMemberLookup( ConstraintSystem &cs, Type keyPathRootTy, ConstraintLocator *locator) { // Let's check whether this is a recursive call to keypath // dynamic member lookup on the same type. if (!locator || !locator->isLastElement()) return false; auto path = locator->getPath(); auto *choiceLoc = cs.getConstraintLocator(locator->getAnchor(), path.drop_back()); if (auto overload = cs.findSelectedOverloadFor(choiceLoc)) { auto baseTy = overload->choice.getBaseType(); // If it's `Foo` vs. `Foo` it doesn't really matter // for dynamic lookup because it's going to be performed on `Foo`. if (baseTy->is() && keyPathRootTy->is()) { auto *baseDecl = baseTy->castTo()->getDecl(); auto *keyPathRootDecl = keyPathRootTy->castTo()->getDecl(); return baseDecl == keyPathRootDecl; } // Previous base type could be r-value because that could be // a base type of subscript "as written" for which we attempt // a dynamic member lookup. auto baseTy1 = baseTy->getRValueType(); // Root type of key path is always wrapped in an l-value // before lookup is performed, so we need to unwrap that. auto baseTy2 = keyPathRootTy->getRValueType(); if (baseTy1->isEqual(baseTy2)) return true; } return false; } /// Given a ValueMember, UnresolvedValueMember, or TypeMember constraint, /// perform a lookup into the specified base type to find a candidate list. /// The list returned includes the viable candidates as well as the unviable /// ones (along with reasons why they aren't viable). /// /// If includeInaccessibleMembers is set to true, this burns compile time to /// try to identify and classify inaccessible members that may be being /// referenced. MemberLookupResult ConstraintSystem:: performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, Type baseTy, FunctionRefInfo functionRefInfo, ConstraintLocator *memberLocator, bool includeInaccessibleMembers) { Type baseObjTy = baseTy->getRValueType(); Type instanceTy = baseObjTy; auto &ctx = getASTContext(); auto memberNode = simplifyLocatorToAnchor(memberLocator); auto memberLoc = memberNode ? memberNode.getStartLoc() : SourceLoc(); if (auto baseObjMeta = baseObjTy->getAs()) { instanceTy = baseObjMeta->getInstanceType(); } MemberLookupResult result; if (instanceTy->isTypeVariableOrMember() || instanceTy->is()) { result.OverallResult = MemberLookupResult::Unsolved; return result; } // Delay member lookup until single-element tuple with pack expansion // is sufficiently resolved. if (isSingleUnlabeledPackExpansionTuple(instanceTy)) { auto elementTy = instanceTy->castTo()->getElementType(0); if (elementTy->is()) { result.OverallResult = MemberLookupResult::Unsolved; return result; } } // Okay, start building up the result list. result.OverallResult = MemberLookupResult::HasResults; // Add key path result. // If we are including inaccessible members, check for the use of a keypath // subscript without a `keyPath:` label. Add it to the result so that it // can be caught by the missing argument label checking later. if (isForKeyPathSubscript(*this, memberLocator) || (mayBeForKeyPathSubscriptWithoutLabel(*this, memberLocator) && includeInaccessibleMembers)) { if (baseTy->isAnyObject()) { result.addUnviable( OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication), MemberLookupResult::UR_KeyPathWithAnyObjectRootType); } else { result.ViableCandidates.push_back( OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication)); } } // If the base type is a tuple type, look for the named or indexed member // of the tuple. if (auto baseTuple = baseObjTy->getAs()) { if (!memberName.isSpecial()) { StringRef nameStr = memberName.getBaseIdentifier().str(); // Accessing `.element` on an abstract tuple materializes a pack. // (deprecated behavior) if (nameStr == "element" && baseTuple->getNumElements() == 1 && isPackExpansionType(baseTuple->getElementType(0))) { auto elementType = baseTuple->getElementType(0); if (elementType->is()) { result.ViableCandidates.push_back( OverloadChoice(baseTy, OverloadChoiceKind::MaterializePack)); } else { assert(elementType->is()); result.OverallResult = MemberLookupResult::Unsolved; } return result; } int fieldIdx = -1; // Resolve a number reference into the tuple type. unsigned Value = 0; if (!nameStr.getAsInteger(10, Value) && Value < baseTuple->getNumElements()) { fieldIdx = Value; } else { fieldIdx = baseTuple->getNamedElementId(memberName.getBaseIdentifier()); } if (fieldIdx != -1) { // Add an overload set that selects this field. result.ViableCandidates.push_back(OverloadChoice(baseTy, fieldIdx)); return result; } } } if (auto *selfTy = instanceTy->getAs()) instanceTy = selfTy->getSelfType(); // Dynamically isolated function types have a magic '.isolation' // member that extracts the isolation value. if (auto *fn = instanceTy->getAs()) { if (fn->getIsolation().isErased() && memberName.isSimpleName(Context.Id_isolation)) { result.ViableCandidates.push_back( OverloadChoice(baseTy, OverloadChoiceKind::ExtractFunctionIsolation)); } } if (!instanceTy->mayHaveMembers()) return result; // If we have a simple name, determine whether there are argument // labels we can use to restrict the set of lookup results. if (baseObjTy->isAnyObject() && memberName.isSimpleName()) { // If we're referencing AnyObject and we have argument labels, put // the argument labels into the name: we don't want to look for // anything else, because the cost of the general search is so // high. if (auto *args = getArgumentList(memberLocator)) { SmallVector scratch; memberName.getFullName() = DeclName(ctx, memberName.getBaseName(), args->getArgumentLabels(scratch)); } } DeclNameRef lookupName = memberName; if (memberName.isCompoundName()) { auto &context = getASTContext(); // Remove any $ prefixes for lookup SmallVector lookupLabels; for (auto label : memberName.getArgumentNames()) { if (label.hasDollarPrefix()) { auto unprefixed = label.str().drop_front(); lookupLabels.push_back(context.getIdentifier(unprefixed)); } else { lookupLabels.push_back(label); } } DeclName unprefixedName(context, memberName.getBaseName(), lookupLabels); lookupName = DeclNameRef(unprefixedName); } // Look for members within the base. LookupResult &lookup = lookupMember(instanceTy, lookupName, memberLoc); // If this is true, we're using type construction syntax (Foo()) rather // than an explicit call to `init` (Foo.init()). bool isImplicitInit = false; TypeBase *favoredType = nullptr; if (memberName.isSimpleName(DeclBaseName::createConstructor())) { SmallVector parts; if (auto anchor = memberLocator->getAnchor()) { auto path = memberLocator->getPath(); if (!path.empty()) if (path.back().getKind() == ConstraintLocator::ConstructorMember) isImplicitInit = true; if (auto *applyExpr = getAsExpr(anchor)) { if (auto *argExpr = applyExpr->getArgs()->getUnlabeledUnaryExpr()) { favoredType = getFavoredType(argExpr); if (!favoredType) { optimizeConstraints(argExpr); favoredType = getFavoredType(argExpr); } } } } } // If we are pattern-matching an enum element and we found any enum elements, // ignore anything that isn't an enum element. bool onlyAcceptEnumElements = false; if (memberLocator && memberLocator->isLastElement() && isa( memberLocator->getLastElementAs() ->getPattern())) { for (const auto &result: lookup) { if (isa(result.getValueDecl())) { onlyAcceptEnumElements = true; break; } } } // If the instance type is String bridged to NSString, compute // the type we'll look in for bridging. Type bridgedType; if (baseObjTy->isString()) { if (Type classType = ctx.getBridgedToObjC(DC, instanceTy)) { bridgedType = classType; } } // Exclude some of the dynamic member choices from results // because using such choices would result in a self-recursive reference. // // This is required because if there are no viable/unviable choices // `performMemberLookup` is going to attempt to lookup inaccessible // members and results would include dynamic member subscripts which // have already been excluded. llvm::SmallPtrSet excludedDynamicMembers; // Local function that adds the given declaration if it is a // reasonable choice. auto addChoice = [&](OverloadChoice candidate) { auto decl = candidate.getDecl(); // Reject circular references immediately. if (decl->isRecursiveValidation()) return; // If the result is invalid, skip it unless solving for code completion // For code completion include the result because we can partially match // against function types that only have one parameter with error type. if (decl->isInvalid() && !isForCodeCompletion()) { result.markErrorAlreadyDiagnosed(); return; } // If we only accept enum elements but this isn't one, ignore it. if (onlyAcceptEnumElements && !isa(decl)) return; // Dig out the instance type and figure out what members of the instance type // we are going to see. auto baseTy = candidate.getBaseType(); const auto baseObjTy = baseTy->getRValueType(); bool hasInstanceMembers = false; bool hasInstanceMethods = false; bool hasStaticMembers = false; Type instanceTy = baseObjTy; if (baseObjTy->is()) { hasStaticMembers = true; } else if (auto baseObjMeta = baseObjTy->getAs()) { instanceTy = baseObjMeta->getInstanceType(); if (baseObjMeta->is()) { // An instance of an existential metatype is a concrete type conforming // to the existential, say Self. Instance members of the concrete type // have type Self -> T -> U, but we don't know what Self is at compile // time so we cannot refer to them. Static methods are fine, on the other // hand -- we already know that they do not have Self or associated type // requirements, since otherwise we would not be able to refer to the // existential metatype in the first place. hasStaticMembers = true; } else if (instanceTy->isExistentialType()) { // A protocol metatype has instance methods with type P -> T -> U, but // not instance properties or static members, unless result type of a // member conforms to this protocol -- the metatype value itself // doesn't give us a witness so there's no static method to bind. hasInstanceMethods = true; hasStaticMembers |= memberLocator->isLastElement(); } else { // Metatypes of nominal types and archetypes have instance methods and // static members, but not instance properties. // FIXME: partial application of properties hasInstanceMethods = true; hasStaticMembers = true; } // If we're at the root of an unevaluated context, we can // reference instance members on the metatype. if (memberLocator && UnevaluatedRootExprs.count(getAsExpr(memberLocator->getAnchor()))) { hasInstanceMembers = true; } } else { // Otherwise, we can access all instance members. hasInstanceMembers = true; hasInstanceMethods = true; } // If the invocation's argument expression has a favored type, // use that information to determine whether a specific overload for // the candidate should be favored. if (isa(decl) && favoredType && result.FavoredChoice == ~0U) { auto *ctor = cast(decl); // Only try and favor monomorphic unary initializers. if (!ctor->isGenericContext()) { if (!ctor->getMethodInterfaceType()->hasError()) { // The constructor might have an error type because we don't skip // invalid decls for code completion auto args = ctor->getMethodInterfaceType() ->castTo() ->getParams(); if (args.size() == 1 && !args[0].hasLabel() && args[0].getPlainType()->isEqual(favoredType)) { if (!isDeclUnavailable(decl, memberLocator)) result.FavoredChoice = result.ViableCandidates.size(); } } } } const auto isUnsupportedExistentialMemberAccess = [&] { // We may not be able to derive a well defined type for an existential // member access if the member's signature references 'Self'. if (instanceTy->isExistentialType()) { switch (isMemberAvailableOnExistential(instanceTy, decl)) { case ExistentialMemberAccessLimitation::Unsupported: // TODO: Write-only accesses are not supported yet. case ExistentialMemberAccessLimitation::WriteOnly: return true; case ExistentialMemberAccessLimitation::ReadOnly: case ExistentialMemberAccessLimitation::None: break; } } return false; }; // See if we have an instance method, instance member or static method, // and check if it can be accessed on our base type. if (decl->isInstanceMember()) { if (baseObjTy->is()) { // `AnyObject` has special semantics, so let's just let it be. // Otherwise adjust base type and reference kind to make it // look as if lookup was done on the instance, that helps // with diagnostics. auto choice = instanceTy->isAnyObject() ? candidate : OverloadChoice(instanceTy, decl, FunctionRefInfo::singleBaseNameApply()); const bool invalidMethodRef = isa(decl) && !hasInstanceMethods; const bool invalidMemberRef = !isa(decl) && !hasInstanceMembers; if (invalidMethodRef || invalidMemberRef) { // If this is definitely an invalid way to reference a method or member // on the metatype, let's stop here. result.addUnviable(choice, MemberLookupResult::UR_InstanceMemberOnType); return; } else if (isUnsupportedExistentialMemberAccess()) { // If the member reference itself is legal, but it turns out to be an // unsupported existential member access, do not make further // assumptions about the correctness of a potential call -- let // the unsupported member access error prevail. result.addUnviable(candidate, MemberLookupResult::UR_UnavailableInExistential); return; } else { // Otherwise, still add an unviable result to the set, because it // could be an invalid call that was supposed to be performed on an // instance of the type. // // New candidate shouldn't affect performance because such // choice would only be attempted when solver is in diagnostic mode. result.addUnviable(choice, MemberLookupResult::UR_InstanceMemberOnType); } } if (auto *UDE = getAsExpr(memberLocator->getAnchor())) { auto *base = UDE->getBase(); if (auto *accessor = DC->getInnermostPropertyAccessorContext()) { if (accessor->isInitAccessor() && isa(base) && accessor->getImplicitSelfDecl() == cast(base)->getDecl()) { bool isValidReference = false; // If name doesn't appear in either `initializes` or `accesses` // then it's invalid instance member. isValidReference |= llvm::any_of( accessor->getInitializedProperties(), [&](VarDecl *prop) { return prop->createNameRef() == memberName; }); isValidReference |= llvm::any_of( accessor->getAccessedProperties(), [&](VarDecl *prop) { return prop->createNameRef() == memberName; }); if (!isValidReference) { result.addUnviable( candidate, MemberLookupResult::UR_UnavailableWithinInitAccessor); return; } } } } // If the underlying type of a typealias is fully concrete, it is legal // to access the type with a protocol metatype base. } else if (instanceTy->isExistentialType() && isa(decl) && !cast(decl) ->getUnderlyingType()->getCanonicalType() ->hasTypeParameter()) { /* We're OK */ } else if (hasStaticMembers && baseObjTy->is() && instanceTy->isExistentialType()) { // Static member lookup on protocol metatype in generic context // requires `Self` of the protocol to be bound to some concrete // type via same-type requirement, otherwise it would be // impossible to find a witness for this member. if (!isa(decl->getDeclContext())) { result.addUnviable(candidate, MemberLookupResult::UR_TypeMemberOnInstance); return; } // Cannot instantiate a protocol or reference a member on // protocol composition type. if (isa(decl) || instanceTy->is()) { result.addUnviable(candidate, MemberLookupResult::UR_TypeMemberOnInstance); return; } if (getConcreteReplacementForProtocolSelfType(decl)) { result.addViable(candidate); } else { result.addUnviable( candidate, MemberLookupResult::UR_InvalidStaticMemberOnProtocolMetatype); } return; } else { if (!hasStaticMembers) { result.addUnviable(candidate, MemberLookupResult::UR_TypeMemberOnInstance); return; } } if (isUnsupportedExistentialMemberAccess()) { result.addUnviable(candidate, MemberLookupResult::UR_UnavailableInExistential); return; } // If we have an rvalue base, make sure that the result isn't 'mutating' // (only valid on lvalues). if (!baseTy->is() && !baseTy->is() && decl->isInstanceMember()) { if (auto *FD = dyn_cast(decl)) if (FD->isMutating()) { result.addUnviable(candidate, MemberLookupResult::UR_MutatingMemberOnRValue); return; } // Subscripts and computed properties are ok on rvalues so long // as the getter is nonmutating. if (auto storage = dyn_cast(decl)) { if (storage->isGetterMutating()) { result.addUnviable(candidate, MemberLookupResult::UR_MutatingGetterOnRValue); return; } } } // Check whether this is overload choice found via keypath // based dynamic member lookup. Since it's unknown upfront // what kind of declaration lookup is going to find, let's // double check here that given keypath is appropriate for it. if (memberLocator) { using KPDynamicMemberElt = LocatorPathElt::KeyPathDynamicMember; if (auto kpElt = memberLocator->getLastElementAs()) { auto *keyPath = kpElt->getKeyPathDecl(); if (isSelfRecursiveKeyPathDynamicMemberLookup(*this, baseTy, memberLocator)) { excludedDynamicMembers.insert(candidate.getDecl()); return; } if (auto *storage = dyn_cast(decl)) { // If this is an attempt to access read-only member via // writable key path, let's fail this choice early. auto &ctx = getASTContext(); if (isReadOnlyKeyPathComponent(storage, SourceLoc()) && (keyPath == ctx.getWritableKeyPathDecl() || keyPath == ctx.getReferenceWritableKeyPathDecl())) { result.addUnviable( candidate, MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember); return; } // A nonmutating setter indicates a reference-writable base, // on the other hand if setter is mutating there is no point // of attempting `ReferenceWritableKeyPath` overload. if (storage->isSetterMutating() && keyPath == ctx.getReferenceWritableKeyPathDecl()) { result.addUnviable(candidate, MemberLookupResult:: UR_ReferenceWritableKeyPathOnMutatingMember); return; } } } } // Otherwise, we're good, add the candidate to the list. result.addViable(candidate); }; // Local function that turns a ValueDecl into a properly configured // OverloadChoice. auto getOverloadChoice = [&](ValueDecl *cand, bool isBridged, bool isUnwrappedOptional, bool isFallbackUnwrap = false) -> OverloadChoice { // If we're looking into an existential type, check whether this // result was found via dynamic lookup. if (instanceTy->isAnyObject()) { assert(cand->getDeclContext()->isTypeContext() && "Dynamic lookup bug"); // We found this declaration via dynamic lookup, record it as such. return OverloadChoice::getDeclViaDynamic(baseTy, cand, functionRefInfo); } // If we have a bridged type, we found this declaration via bridging. if (isBridged) return OverloadChoice::getDeclViaBridge(bridgedType, cand, functionRefInfo); // If we got the choice by unwrapping an optional type, unwrap the base // type. if (isUnwrappedOptional) { auto ovlBaseTy = MetatypeType::get(baseTy->castTo() ->getInstanceType() ->getOptionalObjectType()); return OverloadChoice::getDeclViaUnwrappedOptional( ovlBaseTy, cand, /*isFallback=*/isFallbackUnwrap, functionRefInfo); } // While looking for subscript choices it's possible to find // `subscript(dynamicMember: {Writable}KeyPath)` on types // marked as `@dynamicMemberLookup`, let's mark this candidate // as representing "dynamic lookup" unless it's a direct call // to such subscript (in that case label is expected to match). if (auto *subscript = dyn_cast(cand)) { if (memberLocator && instanceTy->hasDynamicMemberLookupAttribute() && isValidKeyPathDynamicMemberLookup(subscript)) { auto *args = getArgumentList(memberLocator); if (!(args && args->isUnary() && args->getLabel(0) == getASTContext().Id_dynamicMember)) { return OverloadChoice::getDynamicMemberLookup( baseTy, subscript, ctx.getIdentifier("subscript"), /*isKeyPathBased=*/true); } } } return OverloadChoice(baseTy, cand, functionRefInfo); }; // Delay solving member constraint for unapplied methods // where the base type has a conditional Sendable conformance if (Context.LangOpts.hasFeature(Feature::InferSendableFromCaptures)) { auto shouldCheckSendabilityOfBase = [&]() { if (!Context.getProtocol(KnownProtocolKind::Sendable)) return Type(); for (const auto &result : lookup) { auto decl = result.getValueDecl(); if (!isa_and_nonnull(decl)) continue; if (!decl->isInstanceMember() && !decl->getDeclContext()->getSelfProtocolDecl()) continue; auto hasAppliedSelf = decl->hasCurriedSelf() && doesMemberRefApplyCurriedSelf(baseObjTy, decl); auto numApplies = getNumApplications(hasAppliedSelf, functionRefInfo); if (numApplies >= decl->getNumCurryLevels()) continue; return decl->isInstanceMember() ? instanceTy : MetatypeType::get(instanceTy); } return Type(); }; if (Type baseTyToCheck = shouldCheckSendabilityOfBase()) { auto sendableProtocol = Context.getProtocol(KnownProtocolKind::Sendable); auto baseConformance = lookupConformance(baseTyToCheck, sendableProtocol); if (llvm::any_of( baseConformance.getConditionalRequirements(), [&](const auto &req) { if (req.getKind() != RequirementKind::Conformance) return false; return (req.getFirstType()->hasTypeVariable() && (req.getProtocolDecl()->isSpecificProtocol( KnownProtocolKind::Sendable) || req.getProtocolDecl()->isSpecificProtocol( KnownProtocolKind::SendableMetatype))); })) { result.OverallResult = MemberLookupResult::Unsolved; return result; } } } // Add all results from this lookup. for (auto result : lookup) addChoice(getOverloadChoice(result.getValueDecl(), /*isBridged=*/false, /*isUnwrappedOptional=*/false)); // Backward compatibility hack. In Swift 4, `init` and init were // the same name, so you could write "foo.init" to look up a // method or property named `init`. if (!ctx.isSwiftVersionAtLeast(5) && memberName.getBaseName().isConstructor() && !isImplicitInit) { auto &compatLookup = lookupMember(instanceTy, DeclNameRef(ctx.getIdentifier("init")), memberLoc); for (auto result : compatLookup) addChoice(getOverloadChoice(result.getValueDecl(), /*isBridged=*/false, /*isUnwrappedOptional=*/false)); } // If the instance type is a bridged to an Objective-C type, perform // a lookup into that Objective-C type. if (bridgedType) { LookupResult &bridgedLookup = lookupMember(bridgedType, memberName, memberLoc); ModuleDecl *foundationModule = nullptr; for (auto result : bridgedLookup) { // Ignore results from the Objective-C "Foundation" // module. Those core APIs are explicitly provided by the // Foundation module overlay. auto module = result.getValueDecl()->getModuleContext(); if (foundationModule) { if (module == foundationModule) continue; } else if (ClangModuleUnit::hasClangModule(module) && module->getName().str() == "Foundation") { // Cache the foundation module name so we don't need to look // for it again. foundationModule = module; continue; } addChoice(getOverloadChoice(result.getValueDecl(), /*isBridged=*/true, /*isUnwrappedOptional=*/false)); } } // If we have candidates, and we're doing a member lookup for a pattern // match, unwrap optionals and try again to allow implicit creation of // optional "some" patterns (spelled "?"). if (result.ViableCandidates.empty() && result.UnviableCandidates.empty() && memberLocator && memberLocator->isLastElement() && instanceTy->getOptionalObjectType() && baseObjTy->is()) { SmallVector optionals; Type instanceObjectTy = instanceTy->lookThroughAllOptionalTypes(optionals); Type metaObjectType = MetatypeType::get(instanceObjectTy); auto result = performMemberLookup( constraintKind, memberName, metaObjectType, functionRefInfo, memberLocator, includeInaccessibleMembers); result.numImplicitOptionalUnwraps = optionals.size(); result.actualBaseType = metaObjectType; return result; } // If we're looking into a metatype for an unresolved member lookup, look // through optional types. // // FIXME: Unify with the above code path. if (baseObjTy->is() && constraintKind == ConstraintKind::UnresolvedValueMember) { if (auto objectType = instanceTy->getOptionalObjectType()) { // If we don't have a wrapped type yet, we can't look through the optional // type. if (objectType->getAs() && result.ViableCandidates.empty()) { MemberLookupResult result; result.OverallResult = MemberLookupResult::Unsolved; return result; } if (objectType->mayHaveMembers()) { // If there are viable members directly on `Optional`, let's // prioritize them and mark any results found on wrapped type // as a fallback results. bool isFallback = !result.ViableCandidates.empty(); LookupResult &optionalLookup = lookupMember(objectType, memberName, memberLoc); for (auto result : optionalLookup) addChoice(getOverloadChoice(result.getValueDecl(), /*bridged*/ false, /*isUnwrappedOptional=*/true, /*isUnwrapFallback=*/isFallback)); } } } // If we're about to fail lookup because there are no viable candidates // or if all of the candidates come from conditional conformances (which // might not be applicable), and we are looking for members in a type with // the @dynamicMemberLookup attribute, then we resolve a reference to a // `subscript(dynamicMember:)` method and pass the member name as a string // parameter. if (constraintKind == ConstraintKind::ValueMember && memberName.isSimpleName() && !memberName.isSpecial() && instanceTy->hasDynamicMemberLookupAttribute()) { const auto &candidates = result.ViableCandidates; if ((candidates.empty() || allFromConditionalConformances(*this, instanceTy, candidates)) && !isSelfRecursiveKeyPathDynamicMemberLookup(*this, baseTy, memberLocator)) { auto &ctx = getASTContext(); // Recursively look up `subscript(dynamicMember:)` methods in this type. DeclNameRef subscriptName( { ctx, DeclBaseName::createSubscript(), { ctx.Id_dynamicMember } }); auto subscripts = performMemberLookup( constraintKind, subscriptName, baseTy, functionRefInfo, memberLocator, includeInaccessibleMembers); // Reflect the candidates found as `DynamicMemberLookup` results. auto name = memberName.getBaseIdentifier(); for (const auto &candidate : subscripts.ViableCandidates) { auto *SD = cast(candidate.getDecl()); bool isKeyPathBased = isValidKeyPathDynamicMemberLookup(SD); if (isValidStringDynamicMemberLookup(SD, DC->getParentModule()) || isKeyPathBased) result.addViable(OverloadChoice::getDynamicMemberLookup( baseTy, SD, name, isKeyPathBased)); } for (auto index : indices(subscripts.UnviableCandidates)) { auto *SD = cast(subscripts.UnviableCandidates[index].getDecl()); auto choice = OverloadChoice::getDynamicMemberLookup( baseTy, SD, name, isValidKeyPathDynamicMemberLookup(SD)); result.addUnviable(choice, subscripts.UnviableReasons[index]); } } } // If we have no viable or unviable candidates, and we're generating, // diagnostics, rerun the query with inaccessible members included, so we can // include them in the unviable candidates list. if (result.ViableCandidates.empty() && result.UnviableCandidates.empty() && includeInaccessibleMembers) { NameLookupOptions lookupOptions = defaultConstraintSolverMemberLookupOptions; // Local function that looks up additional candidates using the given lookup // options, recording the results as unviable candidates. auto lookupUnviable = [&](NameLookupOptions lookupOptions, MemberLookupResult::UnviableReason reason) -> bool { auto lookup = TypeChecker::lookupMember(DC, instanceTy, memberName, memberLoc, lookupOptions); for (auto entry : lookup) { auto *cand = entry.getValueDecl(); // If the result is invalid, skip it. if (cand->isInvalid()) { result.markErrorAlreadyDiagnosed(); break; } if (excludedDynamicMembers.count(cand)) continue; result.addUnviable(getOverloadChoice(cand, /*isBridged=*/false, /*isUnwrappedOptional=*/false), reason); } return !lookup.empty(); }; // Ignore access control so we get candidates that might have been missed // before. if (lookupUnviable(lookupOptions | NameLookupFlags::IgnoreAccessControl, MemberLookupResult::UR_Inaccessible)) return result; } return result; } /// Determine whether the given type refers to a non-final class (or /// dynamic self of one). static bool isNonFinalClass(Type type) { if (auto dynamicSelf = type->getAs()) type = dynamicSelf->getSelfType(); if (auto classDecl = type->getClassOrBoundGenericClass()) return !classDecl->isSemanticallyFinal(); if (auto archetype = type->getAs()) if (auto super = archetype->getSuperclass()) return isNonFinalClass(super); return type->isExistentialType(); } /// Determine whether given constructor reference is valid or does it require /// any fixes e.g. when base is a protocol metatype. static ConstraintFix *validateInitializerRef(ConstraintSystem &cs, ConstructorDecl *init, ConstraintLocator *locator) { auto anchor = locator->getAnchor(); if (!anchor) return nullptr; // Avoid checking implicit conversions injected by the compiler. if (locator->findFirst()) return nullptr; auto getType = [&cs](Expr *expr) -> Type { return cs.simplifyType(cs.getType(expr))->getRValueType(); }; Expr *baseExpr = nullptr; Type baseType; // Explicit initializer reference e.g. `T.init(...)` or `T.init`. if (auto *UDE = getAsExpr(anchor)) { baseExpr = UDE->getBase(); baseType = getType(baseExpr); if (baseType->is()) { auto instanceType = baseType->getAs()->getInstanceType(); if (!cs.isTypeReference(baseExpr) && instanceType->isExistentialType()) { return AllowInvalidInitRef::onProtocolMetatype( cs, baseType, init, /*isStaticallyDerived=*/true, baseExpr->getSourceRange(), locator); } } // Initializer call e.g. `T(...)` } else if (auto *CE = getAsExpr(anchor)) { baseExpr = CE->getFn(); baseType = getType(baseExpr); // FIXME: Historically, UnresolvedMemberExprs have allowed implicit // construction through a metatype value, but this should probably be // illegal. if (!isa(baseExpr)) { // If this is an initializer call without explicit mention // of `.init` on metatype value. if (auto *AMT = baseType->getAs()) { auto instanceType = AMT->getInstanceType(); if (!cs.isTypeReference(baseExpr)) { if (baseType->is() && instanceType->isAnyExistentialType()) { return AllowInvalidInitRef::onProtocolMetatype( cs, baseType, init, cs.isStaticallyDerivedMetatype(baseExpr), baseExpr->getSourceRange(), locator); } if (!instanceType->isExistentialType() || instanceType->isAnyExistentialType()) { return AllowInvalidInitRef::onNonConstMetatype(cs, baseType, init, locator); } } } } // Initializer reference which requires contextual base type e.g. // `.init(...)`. Could also be a nested type or typealias being constructed // via implicit member syntax, e.g., `let _: Base = .Nested()` where // `Base.Nested: Base`. } else if (auto *UME = getAsExpr(anchor)) { // If we're accessing a nested type to perform the construction implicitly, // then the type we're constructing may not actually be the base of the // UnresolvedMemberExpr--instead, it will be the type of the nested type // member. // We need to find type variable which represents contextual base. auto *baseLocator = cs.getConstraintLocator( UME, locator->isLastElement() ? ConstraintLocator::UnresolvedMember : ConstraintLocator::MemberRefBase); // FIXME: Type variables responsible for contextual base could be cached // in the constraint system to speed up lookup. auto result = llvm::find_if( cs.getTypeVariables(), [&baseLocator](const TypeVariableType *typeVar) { return typeVar->getImpl().getLocator() == baseLocator; }); assert(result != cs.getTypeVariables().end()); baseType = cs.simplifyType(*result)->getRValueType(); // Constraint for member base is formed as '$T.Type[.isLastElement()) baseType = MetatypeType::get(baseType); } else if (getAsExpr(anchor)) { // Key path can't refer to initializers e.g. `\Type.init` return AllowInvalidRefInKeyPath::forRef(cs, baseType, init, locator); } if (!baseType) return nullptr; if (!baseType->is()) { bool applicable = false; // Special case -- in a protocol extension initializer with a class // constrained Self type, 'self' has archetype type, and only // required initializers can be called. if (baseExpr && !baseExpr->isSuperExpr()) { auto &ctx = cs.getASTContext(); if (auto *DRE = dyn_cast(baseExpr->getSemanticsProvidingExpr())) { if (DRE->getDecl()->getName() == ctx.Id_self) { if (getType(DRE)->is()) applicable = true; } } } if (!applicable) return nullptr; } auto instanceType = baseType->getMetatypeInstanceType(); bool isStaticallyDerived = true; // If this is expression like `.init(...)` where base type is // determined by a contextual type. if (!baseExpr) { isStaticallyDerived = !(instanceType->is() || instanceType->is()); // Otherwise this is something like `T.init(...)` } else { isStaticallyDerived = cs.isStaticallyDerivedMetatype(baseExpr); } auto baseRange = baseExpr ? baseExpr->getSourceRange() : SourceRange(); // FIXME: The "hasClangNode" check here is a complete hack. if (isNonFinalClass(instanceType) && !isStaticallyDerived && !init->hasClangNode() && !(init->isRequired() || init->getDeclContext()->getSelfProtocolDecl())) { return AllowInvalidInitRef::dynamicOnMetatype(cs, baseType, init, baseRange, locator); // Constructors cannot be called on a protocol metatype, because there is no // metatype to witness it. } else if (baseType->is() && instanceType->isExistentialType()) { return AllowInvalidInitRef::onProtocolMetatype( cs, baseType, init, isStaticallyDerived, baseRange, locator); } return nullptr; } static ConstraintFix *fixMemberRef( ConstraintSystem &cs, Type baseTy, DeclNameRef memberName, const OverloadChoice &choice, ConstraintLocator *locator, std::optional reason = std::nullopt) { // Not all of the choices handled here are going // to refer to a declaration. if (auto *decl = choice.getDeclOrNull()) { if (auto *CD = dyn_cast(decl)) { if (auto *fix = validateInitializerRef(cs, CD, locator)) return fix; } if (locator->isForKeyPathDynamicMemberLookup() || locator->isForKeyPathComponent() || locator->isKeyPathSubscriptComponent()) { if (auto *fix = AllowInvalidRefInKeyPath::forRef(cs, baseTy, decl, locator)) return fix; } } if (reason) { switch (*reason) { case MemberLookupResult::UR_InstanceMemberOnType: case MemberLookupResult::UR_TypeMemberOnInstance: { return choice.isDecl() ? AllowTypeOrInstanceMember::create( cs, baseTy, choice.getDecl(), memberName, locator) : nullptr; } case MemberLookupResult::UR_Inaccessible: assert(choice.isDecl()); return AllowInaccessibleMember::create(cs, baseTy, choice.getDecl(), memberName, locator); case MemberLookupResult::UR_UnavailableInExistential: { return choice.isDecl() ? AllowMemberRefOnExistential::create( cs, baseTy, choice.getDecl(), memberName, locator) : nullptr; } case MemberLookupResult::UR_MutatingMemberOnRValue: case MemberLookupResult::UR_MutatingGetterOnRValue: { return choice.isDecl() ? AllowMutatingMemberOnRValueBase::create( cs, baseTy, choice.getDecl(), memberName, locator) : nullptr; } // TODO(diagnostics): Add a new fix that is suggests to // add `subscript(dynamicMember: {Writable}KeyPath)` // overload here, that would help if such subscript has // not been provided. case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember: return TreatRValueAsLValue::create(cs, cs.getConstraintLocator(locator)); case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember: break; case MemberLookupResult::UR_KeyPathWithAnyObjectRootType: return AllowAnyObjectKeyPathRoot::create(cs, locator); case MemberLookupResult::UR_InvalidStaticMemberOnProtocolMetatype: return AllowInvalidStaticMemberRefOnProtocolMetatype::create(cs, locator); case MemberLookupResult::UR_UnavailableWithinInitAccessor: return AllowInvalidMemberReferenceInInitAccessor::create(cs, memberName, locator); } } return nullptr; } /// Convert the given enum element pattern into an expression pattern /// and synthesize ~= operator application to find the type of the /// element. static bool inferEnumMemberThroughTildeEqualsOperator( ConstraintSystem &cs, EnumElementPattern *pattern, Type enumTy, Type elementTy, ConstraintLocator *locator) { if (!pattern->hasUnresolvedOriginalExpr()) return true; auto &ctx = cs.getASTContext(); // Retrieve a corresponding ExprPattern which we can solve with ~=. auto *EP = evaluateOrFatal(ctx.evaluator, EnumElementExprPatternRequest{pattern}); auto target = SyntacticElementTarget::forExprPattern(EP); DiagnosticTransaction diagnostics(ctx.Diags); { if (cs.preCheckTarget(target)) { // Skip diagnostics if they are disabled, otherwise it would result in // duplicate diagnostics, since this operation is going to be repeated // in diagnostic mode. if (!cs.shouldAttemptFixes()) diagnostics.abort(); return true; } } cs.setType(EP->getMatchVar(), enumTy); cs.setType(EP, enumTy); if (cs.generateConstraints(target)) return true; // Sub-expression associated with expression pattern is the enum element // access which needs to be connected to the provided element type. cs.addConstraint(ConstraintKind::Conversion, cs.getType(EP->getSubExpr()), elementTy, cs.getConstraintLocator(EP)); cs.setTargetFor(pattern, target); return false; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( ConstraintKind kind, Type baseTy, DeclNameRef member, Type memberTy, DeclContext *useDC, FunctionRefInfo functionRefInfo, ArrayRef outerAlternatives, TypeMatchOptions flags, ConstraintLocatorBuilder locatorB) { // We'd need to record original base type because it might be a type // variable representing another missing member. auto origBaseTy = baseTy; // Resolve the base type, if we can. If we can't resolve the base type, // then we can't solve this constraint. baseTy = simplifyType(baseTy, flags); Type baseObjTy = baseTy->getRValueType(); auto locator = getConstraintLocator(locatorB); auto formUnsolved = [&](bool activate = false) { // If requested, generate a constraint. if (flags.contains(TMF_GenerateConstraints)) { auto *memberRef = Constraint::createMemberOrOuterDisjunction( *this, kind, baseTy, memberTy, member, useDC, functionRefInfo, outerAlternatives, locator); addUnsolvedConstraint(memberRef); if (activate) activateConstraint(memberRef); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; // If the base type of this member lookup is a "hole" there is no // reason to perform a lookup because it wouldn't return any results. if (shouldAttemptFixes()) { auto markMemberTypeAsPotentialHole = [&](Type memberTy) { recordAnyTypeVarAsPotentialHole(simplifyType(memberTy)); }; // If this is an unresolved member ref e.g. `.foo` and its contextual base // type has been determined to be a "hole", let's mark the resulting member // type as a potential hole and continue solving. if (kind == ConstraintKind::UnresolvedValueMember) { // Let's look through all metatypes to find "underlying" type // of this lookup. Type underlyingType = baseObjTy; while (auto *MT = underlyingType->getAs()) { underlyingType = MT->getInstanceType(); } // Let's delay solving this constraint in diagnostic // mode until it's certain that there is no way to // find out what the base type is. if (underlyingType->isTypeVariableOrMember()) return formUnsolved(); // Let's record a fix only if the hole originates either // at the result of the chain (that could happen since solving // of this constraint is delayed until base could be resolved), // or it is certain that base type can't be bound to any other // type but a hole. auto shouldRecordFixForHole = [&](PlaceholderType *baseType) { auto *originator = baseType->getOriginator().dyn_cast(); if (!originator) return false; auto *originatorLoc = originator->getImpl().getLocator(); // It could either be a hole associated directly with the base // or a hole which came from result type of the chain. if (originatorLoc->isLastElement< LocatorPathElt::UnresolvedMemberChainResult>()) { auto *UMCR = castToExpr( originatorLoc->getAnchor()); return UMCR->getChainBase() == getAsExpr(locator->getAnchor()); } return originatorLoc == locator; }; if (auto *hole = underlyingType->getAs()) { if (shouldRecordFixForHole(hole)) { auto *fix = SpecifyBaseTypeForContextualMember::create(*this, member, locator); if (recordFix(fix)) return SolutionKind::Error; } markMemberTypeAsPotentialHole(memberTy); return SolutionKind::Solved; } } else if ((kind == ConstraintKind::ValueMember || kind == ConstraintKind::ValueWitness) && baseObjTy->getMetatypeInstanceType()->isPlaceholder()) { // If base type is a "hole" there is no reason to record any // more "member not found" fixes for chained member references. markMemberTypeAsPotentialHole(memberTy); return SolutionKind::Solved; } } // Special handling of injected references to `makeIterator` and `next` // in for-in loops. if (auto *expr = getAsExpr(locator->getAnchor())) { // `next()` could be wrapped in `await` expression. auto memberRef = getAsExpr(expr->getSemanticsProvidingExpr()); if (memberRef && memberRef->isImplicit() && locator->isLastElement()) { auto &ctx = getASTContext(); // Cannot simplify this constraint yet since we don't know whether // the base type is going to be existential or not. if (baseObjTy->isTypeVariableOrMember()) return formUnsolved(); // Check whether the given dot expression is a reference // to the given name with the given set of argument labels // (aka compound name). auto isRefTo = [&](UnresolvedDotExpr *UDE, Identifier name, ArrayRef labels) { auto refName = UDE->getName().getFullName(); return refName.isCompoundName(name, labels); }; auto *baseExpr = memberRef->getBase(); // Handle `makeIterator` reference. if (getContextualTypePurpose(baseExpr) == CTP_ForEachSequence && isRefTo(memberRef, ctx.Id_makeIterator, /*lables=*/{})) { auto *sequenceProto = cast( getContextualType(baseExpr, /*forConstraint=*/false) ->getAnyNominal()); bool isAsync = sequenceProto->getKnownProtocolKind() == KnownProtocolKind::AsyncSequence; auto *makeIterator = isAsync ? ctx.getAsyncSequenceMakeAsyncIterator() : ctx.getSequenceMakeIterator(); return simplifyValueWitnessConstraint( ConstraintKind::ValueWitness, baseTy, makeIterator, memberTy, useDC, FunctionRefInfo::singleBaseNameApply(), flags, locator); } // Handle `next` reference. if (getContextualTypePurpose(baseExpr) == CTP_ForEachSequence && (isRefTo(memberRef, ctx.Id_next, /*labels=*/{}) || isRefTo(memberRef, ctx.Id_next, /*labels=*/{ "isolation" }))) { auto *iteratorProto = cast( getContextualType(baseExpr, /*forConstraint=*/false) ->getAnyNominal()); bool isAsync = iteratorProto->getKnownProtocolKind() == KnownProtocolKind::AsyncIteratorProtocol; auto loc = locator->getAnchor().getStartLoc(); auto *next = TypeChecker::getForEachIteratorNextFunction(DC, loc, isAsync); return simplifyValueWitnessConstraint( ConstraintKind::ValueWitness, baseTy, next, memberTy, useDC, FunctionRefInfo::singleBaseNameApply(), flags, locator); } } } MemberLookupResult result = performMemberLookup(kind, member, baseTy, functionRefInfo, locator, /*includeInaccessibleMembers*/ shouldAttemptFixes()); switch (result.OverallResult) { case MemberLookupResult::Unsolved: return formUnsolved(); case MemberLookupResult::ErrorAlreadyDiagnosed: case MemberLookupResult::HasResults: // Keep going! break; } SmallVector candidates; // If we found viable candidates, then we're done! if (!result.ViableCandidates.empty()) { // If we had to look in a different type, use that. if (result.actualBaseType) baseTy = result.actualBaseType; // If only possible choice to refer to member is via keypath // dynamic member dispatch, let's delay solving this constraint // until constraint generation phase is complete, because // subscript dispatch relies on presence of function application. if (result.ViableCandidates.size() == 1) { auto &choice = result.ViableCandidates.front(); if (Phase == ConstraintSystemPhase::ConstraintGeneration && choice.isKeyPathDynamicMemberLookup() && member.getBaseName().isSubscript()) { // Let's move this constraint to the active // list so it could be picked up right after // constraint generation is done. return formUnsolved(/*activate=*/true); } } generateOverloadConstraints( candidates, memberTy, result.ViableCandidates, useDC, locator, result.getFavoredIndex(), /*requiresFix=*/false, [&](unsigned, const OverloadChoice &choice) { return fixMemberRef(*this, baseTy, member, choice, locator); }); if (!outerAlternatives.empty()) { // If local scope has a single choice, // it should always be preferred. if (candidates.size() == 1) candidates.front()->setFavored(); // We *might* include any non-members that we found in outer contexts in // some special cases, for backwards compatibility: first, we have to be // looking for one of the special names ('min' or 'max'), and second, all // of the inner (viable) results need to come from conditional // conformances. The second condition is how the problem here was // encountered: a type ('Range') was made to conditionally conform to a // new protocol ('Sequence'), which introduced some extra methods // ('min' and 'max') that shadowed global functions that people regularly // called within extensions to that type (usually adding 'clamp'). bool treatAsViable = (member.isSimpleName("min") || member.isSimpleName("max")) && allFromConditionalConformances(*this, baseTy, result.ViableCandidates); generateOverloadConstraints( candidates, memberTy, outerAlternatives, useDC, locator, std::nullopt, /*requiresFix=*/!treatAsViable, [&](unsigned, const OverloadChoice &) { return treatAsViable ? nullptr : AddQualifierToAccessTopLevelName::create( *this, locator); }); } } if (!result.UnviableCandidates.empty()) { // Generate constraints for unavailable choices if they have a fix, // and disable them by default, they'd get picked up in the "salvage" mode. generateOverloadConstraints( candidates, memberTy, result.UnviableCandidates, useDC, locator, /*favoredChoice=*/std::nullopt, /*requiresFix=*/true, [&](unsigned idx, const OverloadChoice &choice) { return fixMemberRef(*this, baseTy, member, choice, locator, result.UnviableReasons[idx]); }); } // Attempt to record a warning where the unresolved member could be // ambiguous with optional member. e.g. // enum Foo { // case none // } // // let _: Foo? = .none // Although base is inferred as Optional.none // it could be also Foo.none. if (auto *fix = SpecifyBaseTypeForOptionalUnresolvedMember::attempt( *this, kind, baseObjTy, member, functionRefInfo, result, locator)) { (void)recordFix(fix); } // If there were no results from a direct enum lookup, let's attempt // to resolve this member via ~= operator application. if (candidates.empty()) { if (auto patternLoc = locator->getLastElementAs()) { if (auto *enumElement = dyn_cast(patternLoc->getPattern())) { auto enumType = baseObjTy->getMetatypeInstanceType(); // Optional base type does not trigger `~=` synthesis, but it tries // to find member on both `Optional` and its wrapped type. if (!enumType->getOptionalObjectType()) { // If the synthesis of ~= resulted in errors (i.e. broken stdlib) // that would be diagnosed inline, so let's just fall through and // let this situation be diagnosed as a missing member. auto hadErrors = inferEnumMemberThroughTildeEqualsOperator( *this, enumElement, enumType, memberTy, locator); // Let's consider current member constraint solved because it's // replaced by a new set of constraints that would resolve member // type. if (!hadErrors) return SolutionKind::Solved; } } } } if (!candidates.empty()) { addOverloadSet(candidates, locator); return SolutionKind::Solved; } // If the lookup found no hits at all (either viable or unviable), diagnose it // as such and try to recover in various ways. if (shouldAttemptFixes()) { auto fixMissingMember = [&](Type baseTy, Type memberTy, ConstraintLocator *locator) -> SolutionKind { // Let's check whether there are any generic parameters associated with // base type, and record potential holes if so. simplifyType(baseTy).visit([&](Type type) { if (auto *typeVar = type->getAs()) { if (!typeVar->getImpl().hasRepresentativeOrFixed()) recordPotentialHole(typeVar); } }); auto success = [&]() -> SolutionKind { // Record a hole for memberTy to make it possible to form solutions // when contextual result type cannot be deduced e.g. `let _ = x.foo`. if (auto *memberTypeVar = memberTy->getAs()) { if (getFixedType(memberTypeVar)) { // If member has been bound before the base and the base was // incorrect at that (e.g. fallback to default `Any` type), // then we need to re-activate all of the constraints // associated with this member reference otherwise some of // the constraints could be left unchecked in inactive state. // This is especially important for key path expressions because // `key path` constraint can't be retired until all components // are simplified. addTypeVariableConstraintsToWorkList(memberTypeVar); } else if (locator->getAnchor().is() && !getSemanticsProvidingParentExpr( getAsExpr(locator->getAnchor()))) { // If there are no contextual expressions that could provide // a type for the member type variable, let's default it to // a placeholder eagerly so it could be propagated to the // pattern if necessary. recordTypeVariablesAsHoles(memberTypeVar); } else if (locator->isLastElement()) { // Let's handle member patterns specifically because they use // equality instead of argument application constraint, so allowing // them to bind member could mean missing valid hole positions in // the pattern. recordTypeVariablesAsHoles(memberTypeVar); } else { recordPotentialHole(memberTypeVar); } } return SolutionKind::Solved; }; bool alreadyDiagnosed = (result.OverallResult == MemberLookupResult::ErrorAlreadyDiagnosed); auto *fix = DefineMemberBasedOnUse::create(*this, baseTy, member, alreadyDiagnosed, locator); auto instanceTy = baseObjTy->getMetatypeInstanceType(); auto impact = 4; // Impact is higher if the base type is any function type // because function types can't have any members other than self if (instanceTy->is()) { impact += 10; } auto *anchorExpr = getAsExpr(locator->getAnchor()); if (anchorExpr) { if (auto *UDE = dyn_cast(anchorExpr)) { // The issue is related to a missing `Sequence` protocol // conformance if either `makeIterator` or `next` are missing. if (UDE->isImplicit()) { // Missing `makeIterator` since the base is sequence expression. if (getContextualTypePurpose(UDE->getBase()) == CTP_ForEachSequence) return success(); // Missing `next` where the base is result of `makeIterator`. if (auto *base = dyn_cast(UDE->getBase())) { if (auto var = dyn_cast_or_null(base->getDecl())) { if (var->getNameStr().contains("$generator") && (UDE->getName().getBaseIdentifier() == Context.Id_next)) return success(); } } } } // Increasing the impact for missing member in any argument position so // it doesn't affect situations where there are another fixes involved. if (getArgumentLocator(anchorExpr)) impact += 5; } if (recordFix(fix, impact)) return SolutionKind::Error; return success(); }; if (baseObjTy->getOptionalObjectType()) { // If the base type was an optional, look through it. // If the base type is optional because we haven't chosen to force an // implicit optional, don't try to fix it. The IUO will be forced instead. if (auto dotExpr = getAsExpr(locator->getAnchor())) { auto baseExpr = dotExpr->getBase(); if (auto overload = findSelectedOverloadFor(baseExpr)) { auto iuoKind = overload->choice.getIUOReferenceKind(*this); if (iuoKind == IUOReferenceKind::Value) return SolutionKind::Error; } } // Let's check whether the problem is related to optionality of base // type, or there is no member with a given name. result = performMemberLookup(kind, member, baseObjTy->getOptionalObjectType(), functionRefInfo, locator, /*includeInaccessibleMembers*/ true); if (result.OverallResult == MemberLookupResult::Unsolved) return formUnsolved(); // If unwrapped type still couldn't find anything for a given name, // let's fallback to a "not such member" fix. if (result.ViableCandidates.empty() && result.UnviableCandidates.empty()) return fixMissingMember(origBaseTy, memberTy, locator); bool baseIsKeyPathRootType = [&]() { auto keyPathComponent = locator->getLastElementAs(); return keyPathComponent && keyPathComponent->getIndex() == 0; }(); // The result of the member access can either be the expected member type // (for '!' or optional members with '?'), or the original member type // with one extra level of optionality ('?' with non-optional members). auto innerTV = createTypeVariable(locator, TVO_CanBindToLValue | TVO_CanBindToNoEscape); Type optTy = TypeChecker::getOptionalType(SourceLoc(), innerTV); assert(!optTy->hasError()); SmallVector optionalities; auto nonoptionalResult = Constraint::createFixed( *this, ConstraintKind::Bind, UnwrapOptionalBase::create(*this, member, baseObjTy, locator), memberTy, innerTV, locator); optionalities.push_back(nonoptionalResult); if (!baseIsKeyPathRootType) { auto optionalResult = Constraint::createFixed( *this, ConstraintKind::Bind, UnwrapOptionalBase::createWithOptionalResult(*this, member, baseObjTy, locator), optTy, memberTy, locator); optionalities.push_back(optionalResult); } addDisjunctionConstraint(optionalities, locator); // Look through one level of optional. addValueMemberConstraint(baseObjTy->getOptionalObjectType(), member, innerTV, useDC, functionRefInfo, outerAlternatives, locator); return SolutionKind::Solved; } auto solveWithNewBaseOrName = [&](Type baseType, DeclNameRef memberName) -> SolutionKind { return simplifyMemberConstraint(kind, baseType, memberName, memberTy, useDC, functionRefInfo, outerAlternatives, flags | TMF_ApplyingFix, locatorB); }; // If this member reference is a result of a previous fix, let's not allow // any more fixes expect when base is optional, because it could also be // an IUO which requires a separate fix. if (flags.contains(TMF_ApplyingFix)) return SolutionKind::Error; // Check if any property wrappers on the base of the member lookup have // matching members that we can fall back to, or if the type wraps any // properties that have matching members. if (auto *fix = fixPropertyWrapperFailure( *this, baseTy, locator, [&](SelectedOverload overload, VarDecl *decl, Type newBase) { return solveWithNewBaseOrName(newBase, member) == SolutionKind::Solved; })) { return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } // If base is an archetype or metatype of archetype, check for an unintended // extra generic parameter. if (auto archetype = baseTy->getMetatypeInstanceType()->getAs()) { if (auto genericTy = archetype->mapTypeOutOfContext()->getAs()) { for (auto param : DC->getGenericSignatureOfContext() .getGenericParams()) { // Find a param at the same depth and one index past the type we're // dealing with if (param->getDepth() != genericTy->getDepth() || param->getIndex() != genericTy->getIndex() + 1) continue; auto paramDecl = param->getDecl(); if (!paramDecl) continue; auto descriptor = UnqualifiedLookupDescriptor{ DeclNameRef(param->getName()), paramDecl->getDeclContext()->getModuleScopeContext(), SourceLoc(), UnqualifiedLookupFlags::TypeLookup}; auto lookup = evaluateOrDefault( Context.evaluator, UnqualifiedLookupRequest{descriptor}, {}); for (auto &result : lookup) { if (auto proto = dyn_cast_or_null(result.getValueDecl())) { auto result = baseTy->is() ? solveWithNewBaseOrName(ExistentialMetatypeType::get( proto->getDeclaredInterfaceType()), member) : solveWithNewBaseOrName(proto->getDeclaredInterfaceType(), member); if (result == SolutionKind::Solved) return recordFix( DefineMemberBasedOnUnintendedGenericParam::create( *this, baseTy, member, param->getName(), locator)) ? SolutionKind::Error : SolutionKind::Solved; } } } } } if (auto *funcType = baseTy->getAs()) { // We can't really suggest anything useful unless // function takes no arguments, otherwise it // would make sense to report this a missing member. if (funcType->getNumParams() == 0) { auto result = solveWithNewBaseOrName(funcType->getResult(), member); // If there is indeed a member with given name in result type // let's return, otherwise let's fall-through and report // this problem as a missing member. if (result == SolutionKind::Solved) return recordFix(InsertExplicitCall::create( *this, getConstraintLocator( locator, ConstraintLocator::MemberRefBase))) ? SolutionKind::Error : SolutionKind::Solved; } } // Instead of using subscript operator spelled out `subscript` directly. if (member.getBaseName() == getTokenText(tok::kw_subscript)) { auto result = solveWithNewBaseOrName(baseTy, DeclNameRef::createSubscript()); // Looks like it was indeed meant to be a subscript operator. if (result == SolutionKind::Solved) return recordFix(UseSubscriptOperator::create(*this, locator)) ? SolutionKind::Error : SolutionKind::Solved; } // FIXME(diagnostics): This is more of a hack than anything. // Let's not try to suggest that there is no member related to an // obscure underscored type, the real problem would be somewhere // else. This helps to diagnose pattern matching cases. { if (auto *metatype = baseTy->getAs()) { auto instanceTy = metatype->getInstanceType(); if (auto *NTD = instanceTy->getAnyNominal()) { if (NTD->getName() == getASTContext().Id_OptionalNilComparisonType) return SolutionKind::Error; } } } result = performMemberLookup(kind, member, baseTy, functionRefInfo, locator, /*includeInaccessibleMembers*/ true); // FIXME(diagnostics): If there were no viable results, but there are // unviable ones, we'd have to introduce fix for each specific problem. if (!result.UnviableCandidates.empty()) return SolutionKind::Error; // Since member with given base and name doesn't exist, let's try to // fake its presence based on use, that makes it possible to diagnose // problems related to member lookup more precisely. return fixMissingMember(origBaseTy, memberTy, locator); } return SolutionKind::Error; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyValueWitnessConstraint( ConstraintKind kind, Type baseType, ValueDecl *requirement, Type memberType, DeclContext *useDC, FunctionRefInfo functionRefInfo, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { // We'd need to record original base type because it might be a type // variable representing another missing member. auto origBaseType = baseType; auto formUnsolved = [&] { // If requested, generate a constraint. if (flags.contains(TMF_GenerateConstraints)) { auto *witnessConstraint = Constraint::createValueWitness( *this, kind, origBaseType, memberType, requirement, useDC, functionRefInfo, getConstraintLocator(locator)); addUnsolvedConstraint(witnessConstraint); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; auto fail = [&] { // The constraint failed, so mark the member type as a "hole". // We cannot do anything further here. if (!shouldAttemptFixes()) return SolutionKind::Error; recordAnyTypeVarAsPotentialHole(memberType); return SolutionKind::Solved; }; // Resolve the base type, if we can. If we can't resolve the base type, // then we can't solve this constraint. Type baseObjectType = getFixedTypeRecursive( baseType, flags, /*wantRValue=*/true); if (baseObjectType->isTypeVariableOrMember()) { return formUnsolved(); } // If base type is an existential, let's open it before checking // conformance. if (baseObjectType->isExistentialType()) { baseObjectType = ExistentialArchetypeType::get(baseObjectType->getCanonicalType()); } // Check conformance to the protocol. If it doesn't conform, this constraint // fails. Don't attempt to fix it. // FIXME: Look in the constraint system to see if we've resolved the // conformance already? auto proto = requirement->getDeclContext()->getSelfProtocolDecl(); assert(proto && "Value witness constraint for a non-requirement"); auto conformance = lookupConformance(baseObjectType, proto); if (!conformance) return fail(); // Reference the requirement. Type resolvedBaseType = simplifyType(baseType, flags); if (resolvedBaseType->isTypeVariableOrMember()) return formUnsolved(); auto witness = conformance.getWitnessByName(requirement->getName()); if (!witness) return fail(); auto choice = OverloadChoice(resolvedBaseType, witness.getDecl(), functionRefInfo); resolveOverload(getConstraintLocator(locator), memberType, choice, useDC); return SolutionKind::Solved; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyDefaultableConstraint( Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { first = getFixedTypeRecursive(first, flags, true); if (first->isTypeVariableOrMember()) { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( Constraint::create(*this, ConstraintKind::Defaultable, first, second, getConstraintLocator(locator))); return SolutionKind::Solved; } return SolutionKind::Unsolved; } // Otherwise, any type is fine. return SolutionKind::Solved; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyFallbackTypeConstraint( Type defaultableType, Type fallbackType, ArrayRef referencedVars, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { defaultableType = getFixedTypeRecursive(defaultableType, flags, /*wantRValue=*/true); if (defaultableType->isTypeVariableOrMember()) { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint(Constraint::create( *this, ConstraintKind::FallbackType, defaultableType, fallbackType, getConstraintLocator(locator), referencedVars)); return SolutionKind::Solved; } return SolutionKind::Unsolved; } // Propagate placeholders into an inferred closure type. Without this // we'd produce superfluous diagnostics about parameter/result types. if (defaultableType->isPlaceholder() && locator.directlyAt()) { recordTypeVariablesAsHoles(fallbackType); } // Otherwise, any type is fine. return SolutionKind::Solved; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyPropertyWrapperConstraint( Type wrapperType, Type wrappedValueType, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { wrapperType = getFixedTypeRecursive(wrapperType, flags, /*wantRValue=*/true); auto *loc = getConstraintLocator(locator); if (wrapperType->isTypeVariableOrMember()) { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint(Constraint::create( *this, ConstraintKind::PropertyWrapper, wrapperType, wrappedValueType, loc)); return SolutionKind::Solved; } return SolutionKind::Unsolved; } // If the wrapper type is a hole or a dependent member with no type variables, // don't record a fix, because this indicates that there was an error // elsewhere in the constraint system. if (wrapperType->isPlaceholder() || wrapperType->is()) return SolutionKind::Solved; auto *wrappedVar = getAsDecl(locator.getAnchor()); assert(wrappedVar && wrappedVar->hasAttachedPropertyWrapper()); // The wrapper type must be a property wrapper. auto *nominal = wrapperType->getDesugaredType()->getAnyNominal(); if (!(nominal && nominal->getAttrs().hasAttribute())) { if (shouldAttemptFixes()) { auto *fix = AllowInvalidPropertyWrapperType::create( *this, wrapperType, getConstraintLocator(locator)); if (!recordFix(fix)) return SolutionKind::Solved; } return SolutionKind::Error; } auto typeInfo = nominal->getPropertyWrapperTypeInfo(); // Implicit property wrappers must support projected-value initialization. if (wrappedVar->hasImplicitPropertyWrapper() && !(typeInfo.projectedValueVar && typeInfo.hasProjectedValueInit)) { if (shouldAttemptFixes()) { auto *fix = RemoveProjectedValueArgument::create( *this, wrapperType, cast(wrappedVar), getConstraintLocator(locator)); if (!recordFix(fix)) return SolutionKind::Solved; } return SolutionKind::Error; } auto resolvedType = wrapperType->getTypeOfMember(typeInfo.valueVar); if (typeInfo.valueVar->isSettable(nullptr) && typeInfo.valueVar->isSetterAccessibleFrom(DC) && !typeInfo.valueVar->isSetterMutating()) { resolvedType = LValueType::get(resolvedType); } addConstraint(ConstraintKind::Bind, wrappedValueType, resolvedType, locator); return SolutionKind::Solved; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyOneWayConstraint( ConstraintKind kind, Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { // Determine whether the second type can be fully simplified. Only then // will this constraint be resolved. Type secondSimplified = simplifyType(second); if (secondSimplified->hasTypeVariable()) { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( Constraint::create(*this, kind, first, second, getConstraintLocator(locator))); return SolutionKind::Solved; } return SolutionKind::Unsolved; } // Propagate holes through one-way constraints. if (secondSimplified->isPlaceholder()) { recordAnyTypeVarAsPotentialHole(first); return SolutionKind::Solved; } // Translate this constraint into an equality or bind-parameter constraint, // as appropriate. ASSERT(kind == ConstraintKind::OneWayEqual); return matchTypes(first, secondSimplified, ConstraintKind::Equal, flags, locator); } ConstraintSystem::SolutionKind ConstraintSystem::simplifyUnresolvedMemberChainBaseConstraint( Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto resultTy = getFixedTypeRecursive(first, flags, /*wantRValue=*/true); auto baseTy = getFixedTypeRecursive(second, flags, /*wantRValue=*/true); if (baseTy->isTypeVariableOrMember() || resultTy->isTypeVariableOrMember()) { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( Constraint::create(*this, ConstraintKind::UnresolvedMemberChainBase, first, second, getConstraintLocator(locator))); return SolutionKind::Solved; } return SolutionKind::Unsolved; } if (baseTy->is()) { auto *baseExpr = castToExpr(locator.getAnchor()) ->getChainBase(); auto *memberLoc = getConstraintLocator(baseExpr, ConstraintLocator::UnresolvedMember); if (shouldAttemptFixes() && hasFixFor(memberLoc)) return SolutionKind::Solved; auto *memberRef = findResolvedMemberRef(memberLoc); if (memberRef && (memberRef->isStatic() || isa(memberRef))) { return simplifyConformsToConstraint( resultTy, baseTy, ConstraintKind::ConformsTo, getConstraintLocator(memberLoc, ConstraintLocator::MemberRefBase), flags); } } return matchTypes(baseTy, resultTy, ConstraintKind::Equal, flags, locator); } static Type getOpenedResultBuilderTypeFor(ConstraintSystem &cs, ConstraintLocatorBuilder locator) { auto lastElt = locator.last(); if (!lastElt) return Type(); auto argToParamElt = lastElt->getAs(); if (!argToParamElt) return Type(); auto *calleeLocator = cs.getCalleeLocator(cs.getConstraintLocator(locator)); auto selectedOverload = cs.findSelectedOverloadFor(calleeLocator); if (!(selectedOverload && (selectedOverload->choice.getKind() == OverloadChoiceKind::Decl || selectedOverload->choice.getKind() == OverloadChoiceKind::DeclViaUnwrappedOptional))) return Type(); auto *choice = selectedOverload->choice.getDecl(); bool skipCurriedSelf = hasAppliedSelf(cs, selectedOverload->choice); if (choice->hasCurriedSelf() && !skipCurriedSelf) return Type(); if (!choice->hasParameterList()) return Type(); auto *PD = getParameterAt(choice, argToParamElt->getParamIdx()); assert(PD); auto builderType = PD->getResultBuilderType(); if (!builderType) return Type(); // If the builder type has a type parameter, substitute in the type // variables. if (builderType->hasTypeParameter()) { // Find the opened type for this callee and substitute in the type // parameters. auto substitutions = cs.getOpenedTypes(calleeLocator); if (!substitutions.empty()) builderType = cs.openType(builderType, substitutions, locator); assert(!builderType->hasTypeParameter()); } return builderType; } void ConstraintSystem::recordIsolatedParam(ParamDecl *param) { bool inserted = isolatedParams.insert(param).second; ASSERT(inserted); if (solverState) recordChange(SolverTrail::Change::RecordedIsolatedParam(param)); } void ConstraintSystem::removeIsolatedParam(ParamDecl *param) { bool erased = isolatedParams.erase(param); ASSERT(erased); } void ConstraintSystem::recordPreconcurrencyClosure( const ClosureExpr *closure) { bool inserted = preconcurrencyClosures.insert(closure).second; ASSERT(inserted); if (solverState) { recordChange(SolverTrail::Change::RecordedPreconcurrencyClosure( const_cast(closure))); } } void ConstraintSystem::removePreconcurrencyClosure( const ClosureExpr *closure) { bool erased = preconcurrencyClosures.erase(closure); ASSERT(erased); } bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, Type contextualType, ConstraintLocatorBuilder locator) { auto *closureLocator = typeVar->getImpl().getLocator(); auto *closure = castToExpr(closureLocator->getAnchor()); auto *inferredClosureType = getClosureType(closure); // Note if this closure is isolated by preconcurrency. if (hasPreconcurrencyCallee(locator)) recordPreconcurrencyClosure(closure); // Let's look through all optionals associated with contextual // type to make it possible to infer parameter/result type of // the closure faster e.g.: // // func test(_: ((Int) -> Void)?) { // ... // } // // test { $0 + ... } // // In this case dropping optionality from contextual type // `((Int) -> Void)?` allows `resolveClosure` to infer type // of `$0` directly (via `getContextualParamAt`) instead of // having to use type variable inference mechanism. contextualType = contextualType->lookThroughAllOptionalTypes(); auto getContextualParamAt = [&contextualType, &inferredClosureType]( unsigned index) -> std::optional { auto *fnType = contextualType->getAs(); if (!fnType) return std::nullopt; auto numContextualParams = fnType->getNumParams(); if (numContextualParams == 1) { const auto ¶m = fnType->getParams()[0]; if (auto *tuple = param.getPlainType()->getAs()) { // If arity is the same it's a tuple splat which is allowed // for closures (see SE-0110 for more details): // // func test(_: ((Int, Int)) -> Void) {} // test { (arg, _) in // ... // } if (tuple->getNumElements() == inferredClosureType->getNumParams() && param.getParameterFlags().isNone()) { const auto &elt = tuple->getElement(index); return AnyFunctionType::Param(elt.getType(), elt.getName()); } return std::nullopt; } } if (numContextualParams != inferredClosureType->getNumParams() || numContextualParams <= index) return std::nullopt; return fnType->getParams()[index]; }; // Check whether given contextual parameter type could be // used to bind external closure parameter type. auto isSuitableContextualType = [](Type contextualTy) { // We need to wait until contextual type // is fully resolved before binding it. if (contextualTy->isTypeVariableOrMember()) return false; // Cannot propagate pack expansion type from context, // it has to be handled by type matching logic. if (isPackExpansionType(contextualTy)) return false; // If contextual type has an error, let's wait for inference, // otherwise contextual would interfere with diagnostics. if (contextualTy->hasError()) return false; if (isa(contextualTy.getPointer())) { auto underlyingTy = contextualTy->getDesugaredType(); // FIXME: typealias pointing to an existential type is special // because if the typealias has type variables then we'd end up // opening existential from a type with unresolved generic // parameter(s), which CSApply can't currently simplify while // building type-checked AST because `ExistentialArchetypeType` doesn't // propagate flags. Example is as simple as `{ $0.description }` // where `$0` is `Error` that inferred from a (generic) typealias. if (underlyingTy->isExistentialType() && contextualTy->hasTypeVariable()) return false; } return true; }; // If contextual type is not a function type or `Any` and this // closure is used as an argument, let's skip resolution. // // Doing so improves performance if closure is passed as an argument // to a (heavily) overloaded declaration, avoid unrelated errors, // propagate holes, and record a more impactful fix. if (!contextualType->isTypeVariableOrMember() && !(contextualType->is() || contextualType->isAny()) && locator.endsWith()) { if (!shouldAttemptFixes()) return false; assignFixedType(typeVar, PlaceholderType::get(getASTContext(), typeVar)); recordTypeVariablesAsHoles(inferredClosureType); return !recordFix( AllowArgumentMismatch::create(*this, typeVar, contextualType, getConstraintLocator(locator)), /*impact=*/15); } // Determine whether a result builder will be applied. auto resultBuilderType = getOpenedResultBuilderTypeFor(*this, locator); auto *paramList = closure->getParameters(); SmallVector parameters; bool hasIsolatedParam = false; for (unsigned i = 0, n = paramList->size(); i != n; ++i) { auto param = inferredClosureType->getParams()[i]; auto *paramDecl = paramList->get(i); // In case of anonymous or name-only parameters, let's infer // inout/variadic/isolated flags from context, that helps to propagate // type information into the internal type of the parameter and reduces // inference solver has to make. if (!paramDecl->getTypeRepr()) { if (auto contextualParam = getContextualParamAt(i)) { auto flags = param.getParameterFlags(); // Note when a parameter is inferred to be isolated. if (contextualParam->isIsolated() && !flags.isIsolated() && paramDecl) recordIsolatedParam(paramDecl); // Carry-over the ownership specifier from the contextual parameter. auto paramOwnership = contextualParam->getParameterFlags().getOwnershipSpecifier(); // `sending` is already carried over; skip this related ownership kind. if (paramOwnership == ParamSpecifier::ImplicitlyCopyableConsuming) paramOwnership = flags.getOwnershipSpecifier(); param = param.withFlags(flags.withInOut(contextualParam->isInOut()) .withVariadic(contextualParam->isVariadic()) .withIsolated(contextualParam->isIsolated()) .withSending(contextualParam->isSending()) .withOwnershipSpecifier(paramOwnership)); } } if (paramDecl->hasAttachedPropertyWrapper()) { Type backingType; Type wrappedValueType; if (paramDecl->hasImplicitPropertyWrapper()) { if (auto contextualType = getContextualParamAt(i)) { backingType = contextualType->getPlainType(); } else { // There may not be a contextual parameter type if the contextual // type is not a function type or if closure body declares too many // parameters. auto *paramLoc = getConstraintLocator(closure, LocatorPathElt::TupleElement(i)); backingType = createTypeVariable(paramLoc, TVO_CanBindToHole); } wrappedValueType = createTypeVariable(getConstraintLocator(paramDecl), TVO_CanBindToHole | TVO_CanBindToLValue); } else { auto *wrapperAttr = paramDecl->getOutermostAttachedPropertyWrapper(); auto wrapperType = paramDecl->getAttachedPropertyWrapperType(0); backingType = replaceInferableTypesWithTypeVars( wrapperType, getConstraintLocator(wrapperAttr->getTypeRepr())); wrappedValueType = computeWrappedValueType(paramDecl, backingType); } auto *backingVar = paramDecl->getPropertyWrapperBackingProperty(); setType(backingVar, backingType); auto *localWrappedVar = paramDecl->getPropertyWrapperWrappedValueVar(); setType(localWrappedVar, wrappedValueType); if (auto *projection = paramDecl->getPropertyWrapperProjectionVar()) { setType(projection, computeProjectedValueType(paramDecl, backingType)); } if (!paramDecl->getName().hasDollarPrefix()) { if (generateWrappedPropertyTypeConstraints(paramDecl, backingType, param.getParameterType())) return false; } auto result = applyPropertyWrapperToParameter(backingType, param.getParameterType(), paramDecl, paramDecl->getName(), ConstraintKind::Equal, getConstraintLocator(closure), getConstraintLocator(closure)); if (result.isFailure()) return false; } Type internalType; if (paramDecl->getTypeRepr()) { // Internal type is the type used in the body of the closure, // so "external" type translates to it as follows: // - `Int...` -> `[Int]`, // - `inout Int` -> `@lvalue Int`. internalType = param.getParameterType(); } else { auto *paramLoc = getConstraintLocator(closure, LocatorPathElt::TupleElement(i)); auto *typeVar = createTypeVariable(paramLoc, TVO_CanBindToLValue | TVO_CanBindToNoEscape); // If external parameter is variadic it translates into an array in // the body of the closure. internalType = param.isVariadic() ? VariadicSequenceType::get(typeVar) : Type(typeVar); auto externalType = param.getOldType(); // Performance optimization. // // If there is a concrete contextual type we could use, let's bind // it to the external type right away because internal type has to // be equal to that type anyway (through `BindParam` on external type // i.e. bind param conv ). // // Note: it's correct to avoid doing this, but it would result // in (a lot) more checking since solver would have to re-discover, // re-attempt and fail parameter type while solving for overloaded // choices in the body. if (auto contextualParam = getContextualParamAt(i)) { auto paramTy = simplifyType(contextualParam->getOldType()); if (isSuitableContextualType(paramTy)) addConstraint(ConstraintKind::Bind, externalType, paramTy, paramLoc); } addConstraint( ConstraintKind::BindParam, externalType, typeVar, paramLoc); } hasIsolatedParam |= param.isIsolated(); setType(paramDecl, internalType); parameters.push_back(param); } // Propagate @Sendable from the contextual type to the closure. auto closureExtInfo = inferredClosureType->getExtInfo(); if (auto contextualFnType = contextualType->getAs()) { if (contextualFnType->isSendable()) closureExtInfo = closureExtInfo.withSendable(); } // Propagate sending result from the contextual type to the closure. if (auto contextualFnType = contextualType->getAs()) { if (contextualFnType->hasExtInfo() && contextualFnType->hasSendingResult()) closureExtInfo = closureExtInfo.withSendingResult(); } // Isolated parameters override any other kind of isolation we might infer. if (hasIsolatedParam) { closureExtInfo = closureExtInfo.withIsolation( FunctionTypeIsolation::forParameter()); } auto closureType = FunctionType::get(parameters, inferredClosureType->getResult(), closureExtInfo); assignFixedType(typeVar, closureType); // If there is a result builder to apply, do so now. if (resultBuilderType) { if (auto result = matchResultBuilder( closure, resultBuilderType, closureType->getResult(), ConstraintKind::Conversion, contextualType, locator)) { return result->isSuccess(); } } SyntacticElementTarget target(closure, contextualType); setTargetFor(closure, target); // Generate constraints from the body of this closure. return !generateConstraints(AnyFunctionRef{closure}, closure->getBody()); } bool ConstraintSystem::resolvePackExpansion(TypeVariableType *typeVar, Type contextualType) { assert(typeVar->getImpl().isPackExpansion()); auto *locator = typeVar->getImpl().getLocator(); Type openedExpansionType = locator->castLastElementTo() .getOpenedType(); assignFixedType(typeVar, openedExpansionType); return true; } bool ConstraintSystem::resolveTapBody(TypeVariableType *typeVar, Type contextualType, ConstraintLocatorBuilder locator) { auto *tapLoc = typeVar->getImpl().getLocator(); auto *tapExpr = castToExpr(tapLoc->getAnchor()); // Assign a type to tap expression itself. assignFixedType(typeVar, contextualType); // Set type to `$interpolation` variable declared in the body of tap // expression. setType(tapExpr->getVar(), contextualType); // With all of the contextual information recorded in the constraint system, // it's time to generate constraints for the body of this tap expression. return !generateConstraints(tapExpr); } bool ConstraintSystem::resolveKeyPath(TypeVariableType *typeVar, Type contextualType, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { // Key path types recently gained Copyable, Escapable requirements. // The solver cannot account for that during inference because Root // and Value types are required to be only resolved enough to infer // a capability of a key path itself. if (auto *BGT = contextualType->getAs()) { auto keyPathTy = openUnboundGenericType( BGT->getDecl(), BGT->getParent(), locator, /*isTypeResolution=*/false); assignFixedType( typeVar, keyPathTy, /*updateState=*/true, /*notifyInference=*/!flags.contains(TMF_BindingTypeVariable)); addConstraint(ConstraintKind::Equal, keyPathTy, contextualType, locator); return true; } assignFixedType(typeVar, contextualType, /*updateState=*/true, /*notifyInference=*/!flags.contains(TMF_BindingTypeVariable)); return true; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyDynamicTypeOfConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); // Local function to form an unsolved result. auto formUnsolved = [&] { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( Constraint::create(*this, ConstraintKind::DynamicTypeOf, type1, type2, getConstraintLocator(locator))); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; // Solve forward. type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true); if (!type2->isTypeVariableOrMember()) { Type dynamicType2; if (type2->isAnyExistentialType()) { dynamicType2 = ExistentialMetatypeType::get(type2); } else { dynamicType2 = MetatypeType::get(type2); } return matchTypes(type1, dynamicType2, ConstraintKind::Bind, subflags, locator); } // Okay, can't solve forward. See what we can do backwards. type1 = getFixedTypeRecursive(type1, flags, /*wantRValue=*/true); if (type1->isTypeVariableOrMember()) return formUnsolved(); // If we have an existential metatype, that's good enough to solve // the constraint. if (auto metatype1 = type1->getAs()) return matchTypes(metatype1->getInstanceType(), type2, ConstraintKind::Bind, subflags, locator); // If we have a normal metatype, we can't solve backwards unless we // know what kind of object it is. if (auto metatype1 = type1->getAs()) { Type instanceType1 = getFixedTypeRecursive(metatype1->getInstanceType(), true); if (instanceType1->isTypeVariableOrMember()) return formUnsolved(); return matchTypes(instanceType1, type2, ConstraintKind::Bind, subflags, locator); } // It's definitely not either kind of metatype, so we can // report failure right away. return SolutionKind::Error; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyBridgingConstraint(Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); /// Form an unresolved result. auto formUnsolved = [&] { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( Constraint::create(*this, ConstraintKind::BridgingConversion, type1, type2, getConstraintLocator(locator))); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; // Local function to look through optional types. It produces the // fully-unwrapped type and a count of the total # of optional types that were // unwrapped. auto unwrapType = [&](Type type) -> std::pair { unsigned count = 0; while (Type objectType = type->getOptionalObjectType()) { ++count; TypeMatchOptions unusedOptions; type = getFixedTypeRecursive(objectType, unusedOptions, /*wantRValue=*/true); } return { type, count }; }; const auto rawType1 = type1; type1 = getFixedTypeRecursive(type1, flags, /*wantRValue=*/true); type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true); if (type1->isTypeVariableOrMember() || type2->isTypeVariableOrMember()) return formUnsolved(); // Noncopyable & Nonescapable types can't be involved in bridging conversions // since a bridged type assumes such abilities are granted. if (!type1->hasTypeVariable() && (type1->isNoncopyable() || !type1->isEscapable())) { return SolutionKind::Error; } Type unwrappedFromType; unsigned numFromOptionals; std::tie(unwrappedFromType, numFromOptionals) = unwrapType(type1); Type unwrappedToType; unsigned numToOptionals; std::tie(unwrappedToType, numToOptionals) = unwrapType(type2); if (unwrappedFromType->isTypeVariableOrMember() || unwrappedToType->isTypeVariableOrMember()) return formUnsolved(); // Update the score. increaseScore(SK_UserConversion, locator); // FIXME: Use separate score kind? if (worseThanBestSolution()) { return SolutionKind::Error; } // Local function to count the optional injections that will be performed // after the bridging conversion. auto countOptionalInjections = [&] { if (numToOptionals > numFromOptionals) increaseScore(SK_ValueToOptional, locator, numToOptionals - numFromOptionals); }; // Anything can be explicitly converted to AnyObject using the universal // bridging conversion. This allows both extraneous optionals in the source // (because optionals themselves can be boxed for AnyObject) and in the // destination (we'll perform the extra injections at the end). if (unwrappedToType->isAnyObject()) { countOptionalInjections(); return SolutionKind::Solved; } // In a previous version of Swift, we could accidentally drop the coercion // constraint in certain cases. In most cases this led to either miscompiles // or crashes later down the pipeline, but for coercions between collections // we generated somewhat reasonable code that performed a force cast. To // maintain compatibility with that behavior, allow the coercion between // two collections, but add a warning fix telling the user to use as! or as? // instead. In Swift 6 mode, this becomes an error. // // We only need to perform this compatibility logic if this is a coercion of // something that isn't a collection expr (as collection exprs would have // crashed in codegen due to CSApply peepholing them). Additionally, the LHS // type must be a (potentially optional) type variable, as only such a // constraint could have been previously been left unsolved. auto canUseCompatFix = [&]() { if (Context.isSwiftVersionAtLeast(6)) return false; if (!rawType1->lookThroughAllOptionalTypes()->isTypeVariableOrMember()) return false; SmallVector elts; auto anchor = locator.getLocatorParts(elts); if (elts.empty() || !elts.back().is()) return false; auto *coercion = getAsExpr(anchor); if (!coercion) return false; auto *subject = coercion->getSubExpr(); while (auto *paren = dyn_cast(subject)) subject = paren->getSubExpr(); return !isa(subject); }(); // Unless we're allowing the collection compatibility fix, the source cannot // be more optional than the destination. if (!canUseCompatFix && numFromOptionals > numToOptionals) return SolutionKind::Error; auto makeCollectionResult = [&](SolutionKind result) -> SolutionKind { // If we encountered an error and can use the compatibility fix, do so. if (canUseCompatFix) { if (numFromOptionals > numToOptionals || result == SolutionKind::Error) { auto *loc = getConstraintLocator(locator); auto *fix = AllowCoercionToForceCast::create(*this, type1, type2, loc); return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } } return result; }; // Bridging the elements of an array. if (auto fromElement = unwrappedFromType->getArrayElementType()) { if (auto toElement = unwrappedToType->getArrayElementType()) { countOptionalInjections(); auto result = simplifyBridgingConstraint( fromElement, toElement, subflags, locator.withPathElement(LocatorPathElt::GenericArgument(0))); return makeCollectionResult(result); } } // Bridging the keys/values of a dictionary. if (auto fromKeyValue = isDictionaryType(unwrappedFromType)) { if (auto toKeyValue = isDictionaryType(unwrappedToType)) { ConstraintFix *compatFix = nullptr; if (canUseCompatFix) { compatFix = AllowCoercionToForceCast::create( *this, type1, type2, getConstraintLocator(locator)); } addExplicitConversionConstraint(fromKeyValue->first, toKeyValue->first, ForgetChoice, locator.withPathElement( LocatorPathElt::GenericArgument(0)), compatFix); addExplicitConversionConstraint(fromKeyValue->second, toKeyValue->second, ForgetChoice, locator.withPathElement( LocatorPathElt::GenericArgument(1)), compatFix); countOptionalInjections(); return makeCollectionResult(SolutionKind::Solved); } } // Bridging the elements of a set. if (auto fromElement = isSetType(unwrappedFromType)) { if (auto toElement = isSetType(unwrappedToType)) { countOptionalInjections(); auto result = simplifyBridgingConstraint( *fromElement, *toElement, subflags, locator.withPathElement(LocatorPathElt::GenericArgument(0))); return makeCollectionResult(result); } } // The source cannot be more optional than the destination, because bridging // conversions don't allow us to implicitly check for a value in the optional. if (numFromOptionals > numToOptionals) { return SolutionKind::Error; } // Explicit bridging from a value type to an Objective-C class type. auto &ctx = getASTContext(); if (unwrappedFromType->isPotentiallyBridgedValueType() && (unwrappedToType->isBridgeableObjectType() || (unwrappedToType->isExistentialType() && !unwrappedToType->isAny()))) { countOptionalInjections(); if (Type classType = ctx.getBridgedToObjC(DC, unwrappedFromType)) { return matchTypes(classType, unwrappedToType, ConstraintKind::Conversion, subflags, locator); } } // Bridging from an Objective-C class type to a value type. // Note that specifically require a class or class-constrained archetype // here, because archetypes cannot be bridged. if (unwrappedFromType->mayHaveSuperclass() && unwrappedToType->isPotentiallyBridgedValueType()) { Type bridgedValueType; if (auto objcClass = ctx.getBridgedToObjC(DC, unwrappedToType, &bridgedValueType)) { // Bridging NSNumber to NSValue is one-way, since there are multiple Swift // value types that bridge to those object types. It requires a checked // cast to get back. if (ctx.isObjCClassWithMultipleSwiftBridgedTypes(objcClass)) return SolutionKind::Error; // If the bridged value type is generic, the generic arguments // must either match or be bridged. // FIXME: This should be an associated type of the protocol. auto &ctx = getASTContext(); if (auto fromBGT = unwrappedToType->getAs()) { if (fromBGT->isArray()) { // [AnyObject] addConstraint(ConstraintKind::Bind, fromBGT->getGenericArgs()[0], ctx.getAnyObjectType(), getConstraintLocator(locator.withPathElement( LocatorPathElt::GenericArgument(0)))); } else if (fromBGT->isDictionary()) { // [NSObject : AnyObject] auto nsObjectType = ctx.getNSObjectType(); if (!nsObjectType) { // Not a bridging case. Should we detect this earlier? return SolutionKind::Error; } addConstraint(ConstraintKind::Bind, fromBGT->getGenericArgs()[0], nsObjectType, getConstraintLocator( locator.withPathElement( LocatorPathElt::GenericArgument(0)))); addConstraint(ConstraintKind::Bind, fromBGT->getGenericArgs()[1], ctx.getAnyObjectType(), getConstraintLocator( locator.withPathElement( LocatorPathElt::GenericArgument(1)))); } else if (fromBGT->isSet()) { auto nsObjectType = ctx.getNSObjectType(); if (!nsObjectType) { // Not a bridging case. Should we detect this earlier? return SolutionKind::Error; } addConstraint(ConstraintKind::Bind, fromBGT->getGenericArgs()[0], nsObjectType, getConstraintLocator( locator.withPathElement( LocatorPathElt::GenericArgument(0)))); } else { // Nothing special to do; matchTypes will match generic arguments. } } // Make sure we have the bridged value type. if (matchTypes(unwrappedToType, bridgedValueType, ConstraintKind::Bind, subflags, locator).isFailure()) return SolutionKind::Error; countOptionalInjections(); return matchTypes(unwrappedFromType, objcClass, ConstraintKind::Subtype, subflags, locator); } } return SolutionKind::Error; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyEscapableFunctionOfConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); // Local function to form an unsolved result. auto formUnsolved = [&] { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( Constraint::create(*this, ConstraintKind::EscapableFunctionOf, type1, type2, getConstraintLocator(locator))); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true); if (auto fn2 = type2->getAs()) { // Solve forward by binding the other type variable to the escapable // variation of this type. auto fn1 = fn2->withExtInfo(fn2->getExtInfo().withNoEscape(false)); return matchTypes(type1, fn1, ConstraintKind::Bind, subflags, locator); } if (!type2->isTypeVariableOrMember()) // We definitely don't have a function, so bail. return SolutionKind::Error; type1 = getFixedTypeRecursive(type1, flags, /*wantRValue=*/true); if (auto fn1 = type1->getAs()) { // We should have the escaping end of the relation. if (fn1->getExtInfo().isNoEscape()) return SolutionKind::Error; // Solve backward by binding the other type variable to the noescape // variation of this type. auto fn2 = fn1->withExtInfo(fn1->getExtInfo().withNoEscape(true)); return matchTypes(type2, fn2, ConstraintKind::Bind, subflags, locator); } if (!type1->isTypeVariableOrMember()) // We definitely don't have a function, so bail. return SolutionKind::Error; return formUnsolved(); } ConstraintSystem::SolutionKind ConstraintSystem::simplifyOpenedExistentialOfConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true); if (type2->isAnyExistentialType()) { // We have the existential side. Produce an opened archetype and bind // type1 to it. Type openedTy = openAnyExistentialType(type2, getConstraintLocator(locator)).first; return matchTypes(type1, openedTy, ConstraintKind::Bind, subflags, locator); } if (!type2->isTypeVariableOrMember()) // We definitely don't have an existential, so bail. return SolutionKind::Error; // If type1 is constrained to anything concrete, the constraint fails. // It can only be bound to a type we opened for it. type1 = getFixedTypeRecursive(type1, flags, /*wantRValue=*/true); if (!type1->isTypeVariableOrMember()) return SolutionKind::Error; if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( Constraint::create(*this, ConstraintKind::OpenedExistentialOf, type1, type2, getConstraintLocator(locator))); return SolutionKind::Solved; } return SolutionKind::Unsolved; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyKeyPathConstraint( Type keyPathTy, Type rootTy, Type valueTy, ArrayRef componentTypeVars, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto subflags = getDefaultDecompositionOptions(flags); keyPathTy = getFixedTypeRecursive(keyPathTy, /*want rvalue*/ true); auto formUnsolved = [&]() -> SolutionKind { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint(Constraint::create( *this, ConstraintKind::KeyPath, keyPathTy, rootTy, valueTy, getConstraintLocator(locator), componentTypeVars)); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; if (keyPathTy->isTypeVariableOrMember()) return formUnsolved(); auto tryMatchRootAndValueFromContextualType = [&](Type contextualTy) -> bool { Type contextualRootTy = Type(), contextualValueTy = Type(); // Placeholders are only allowed in the diagnostic mode so it's // okay to simply return `true` here. if (contextualTy->isPlaceholder()) return true; // Situations like `any KeyPath<...> & Sendable`. if (contextualTy->isExistentialType()) { contextualTy = contextualTy->getExistentialLayout().explicitSuperclass; assert(contextualTy); } if (auto bgt = contextualTy->getAs()) { // We can get root and value from a concrete key path type. assert(bgt->isKeyPath() || bgt->isWritableKeyPath() || bgt->isReferenceWritableKeyPath()); contextualRootTy = bgt->getGenericArgs()[0]; contextualValueTy = bgt->getGenericArgs()[1]; } if (auto fnTy = contextualTy->getAs()) { assert(fnTy->getParams().size() == 1); // Key paths may be converted to a function of compatible type. We will // later form from this key path an implicit closure of the form // `{ root in root[keyPath: kp] }` so any conversions that are valid with // a source type of `(Root) -> Value` should be valid here too. auto rootParam = AnyFunctionType::Param(rootTy); auto kpFnTy = FunctionType::get(rootParam, valueTy, fnTy->getExtInfo()); // Note: because the keypath is applied to `root` as a parameter internal // to the closure, we use the function parameter's "parameter type" rather // than the raw type. This enables things like: // ``` // let countKeyPath: (String...) -> Int = \.count // ``` auto paramTy = fnTy->getParams()[0].getParameterType(); auto paramParam = AnyFunctionType::Param(paramTy); auto paramFnTy = FunctionType::get(paramParam, fnTy->getResult(), fnTy->getExtInfo()); return !matchTypes(kpFnTy, paramFnTy, ConstraintKind::Conversion, subflags, locator).isFailure(); } assert(contextualRootTy && contextualValueTy); if (matchTypes(rootTy, contextualRootTy, ConstraintKind::Bind, subflags, locator.withPathElement(ConstraintLocator::KeyPathRoot)) .isFailure()) return false; if (matchTypes(valueTy, contextualValueTy, ConstraintKind::Bind, subflags, locator.withPathElement(ConstraintLocator::KeyPathValue)) .isFailure()) return false; return true; }; // If key path has to be converted to a function, let's check that // the contextual type has precisely one parameter. if (auto *fnTy = keyPathTy->getAs()) { increaseScore(SK_FunctionConversion, locator); // Key paths never throw, so if the function has a thrown error type // that is a type variable, infer it to be Never. if (auto thrownError = fnTy->getThrownError()) { if (thrownError->isTypeVariableOrMember()) { (void)matchTypes(thrownError, getASTContext().getNeverType(), ConstraintKind::Equal, TMF_GenerateConstraints, locator); } } if (fnTy->getParams().size() != 1) { if (!shouldAttemptFixes()) return SolutionKind::Error; recordAnyTypeVarAsPotentialHole(rootTy); recordAnyTypeVarAsPotentialHole(valueTy); auto *fix = AllowMultiArgFuncKeyPathMismatch::create( *this, fnTy, getConstraintLocator(locator)); // Pretend the keypath type got resolved and move on. return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } } // If we have a hole somewhere in the key path, the solver won't be able to // infer the key path type. So let's just assume this is solved. if (shouldAttemptFixes()) { auto keyPath = castToExpr(locator.getAnchor()); if (hasFixFor(getConstraintLocator(keyPath), FixKind::AllowKeyPathWithoutComponents)) return SolutionKind::Solved; // If the root type has been bound to a hole, we cannot infer it. if (getFixedTypeRecursive(rootTy, /*wantRValue*/ true)->isPlaceholder()) return SolutionKind::Solved; } return tryMatchRootAndValueFromContextualType(keyPathTy) ? SolutionKind::Solved : SolutionKind::Error; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyKeyPathApplicationConstraint( Type keyPathTy, Type rootTy, Type valueTy, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); keyPathTy = getFixedTypeRecursive(keyPathTy, flags, /*wantRValue=*/true); auto unsolved = [&]() -> SolutionKind { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint(Constraint::create(*this, ConstraintKind::KeyPathApplication, keyPathTy, rootTy, valueTy, getConstraintLocator(locator))); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; // When locator points to a KeyPathDynamicMemberLookup, reject the // key path application. if (locator.endsWith()) return SolutionKind::Error; if (keyPathTy->isAnyKeyPath()) { // Read-only keypath, whose projected value is upcast to `Any?`. // The root type can be anything. Type resultTy = getASTContext().getAnyExistentialType(); resultTy = OptionalType::get(resultTy); return matchTypes(resultTy, valueTy, ConstraintKind::Bind, subflags, locator); } if (keyPathTy->isPlaceholder()) { if (rootTy->hasTypeVariable()) { recordAnyTypeVarAsPotentialHole(rootTy); } if (valueTy->hasTypeVariable()) { recordAnyTypeVarAsPotentialHole(valueTy); } return SolutionKind::Solved; } if (auto bgt = keyPathTy->getAs()) { // We have the key path type. Match it to the other ends of the constraint. auto kpRootTy = bgt->getGenericArgs()[0]; // Try to match the root type. rootTy = getFixedTypeRecursive(rootTy, flags, /*wantRValue=*/false); auto matchRoot = [&](ConstraintKind kind) -> bool { auto rootMatches = matchTypes(rootTy, kpRootTy, kind, subflags, locator.withPathElement(LocatorPathElt::KeyPathRoot())); switch (rootMatches) { case SolutionKind::Error: return false; case SolutionKind::Solved: return true; case SolutionKind::Unsolved: llvm_unreachable("should have generated constraints"); } llvm_unreachable("unhandled match"); }; if (bgt->isPartialKeyPath()) { // Read-only keypath, whose projected value is upcast to `Any`. auto resultTy = getASTContext().getAnyExistentialType(); if (!matchRoot(ConstraintKind::Conversion)) return SolutionKind::Error; return matchTypes(resultTy, valueTy, ConstraintKind::Bind, subflags, locator); } if (bgt->getGenericArgs().size() < 2) return SolutionKind::Error; auto kpValueTy = bgt->getGenericArgs()[1]; /// Solve for an rvalue base. auto solveRValue = [&]() -> ConstraintSystem::SolutionKind { // An rvalue base can be converted to a supertype. return matchTypes(kpValueTy, valueTy, ConstraintKind::Bind, subflags, locator); }; /// Solve for a base whose lvalueness is to be determined. auto solveUnknown = [&]() -> ConstraintSystem::SolutionKind { if (matchTypes(kpValueTy, valueTy, ConstraintKind::Equal, subflags, locator).isFailure()) return SolutionKind::Error; return unsolved(); }; /// Solve for an lvalue base. auto solveLValue = [&]() -> ConstraintSystem::SolutionKind { return matchTypes(LValueType::get(kpValueTy), valueTy, ConstraintKind::Bind, subflags, locator); }; if (bgt->isKeyPath()) { // Read-only keypath. if (!matchRoot(ConstraintKind::Conversion)) return SolutionKind::Error; return solveRValue(); } if (bgt->isWritableKeyPath()) { // Writable keypath. The result can be an lvalue if the root was. // We can't convert the base without giving up lvalue-ness, though. if (!matchRoot(ConstraintKind::Equal)) return SolutionKind::Error; if (rootTy->is()) return solveLValue(); if (rootTy->isTypeVariableOrMember()) // We don't know whether the value is an lvalue yet. return solveUnknown(); return solveRValue(); } if (bgt->isReferenceWritableKeyPath()) { if (!matchRoot(ConstraintKind::Conversion)) return SolutionKind::Error; // Reference-writable keypath. The result can always be an lvalue. return solveLValue(); } // Otherwise, we don't have a key path type at all. return SolutionKind::Error; } if (!keyPathTy->isTypeVariableOrMember()) { if (shouldAttemptFixes()) { auto *fix = IgnoreKeyPathSubscriptIndexMismatch::create( *this, keyPathTy, getConstraintLocator(locator)); recordAnyTypeVarAsPotentialHole(valueTy); return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } return SolutionKind::Error; } return unsolved(); } bool ConstraintSystem::simplifyAppliedOverloadsImpl( Constraint *disjunction, TypeVariableType *fnTypeVar, FunctionType *argFnType, unsigned numOptionalUnwraps, ConstraintLocatorBuilder locator) { // Don't attempt to filter overloads when solving for code completion // because presence of code completion token means that any call // could be malformed e.g. missing arguments e.g. `foo([.#^MEMBER^#` if (isForCodeCompletion()) { bool ArgContainsCCTypeVar = Type(argFnType).findIf(isCodeCompletionTypeVar); if (ArgContainsCCTypeVar || isCodeCompletionTypeVar(fnTypeVar)) { return false; } } if (shouldAttemptFixes()) { auto arguments = argFnType->getParams(); bool allHoles = arguments.size() > 0 && llvm::all_of(arguments, [&](const AnyFunctionType::Param &arg) -> bool { auto argType = arg.getPlainType(); if (argType->isPlaceholder()) return true; if (auto *typeVar = argType->getAs()) return hasFixFor(typeVar->getImpl().getLocator()); return false; }); // If this is an operator application and all of the arguments are holes, // let's disable all but one overload to make sure holes don't cause // performance problems because hole could be bound to any type. // // Non-operator calls are exempted because they have fewer overloads, // and it's possible to filter them based on labels. if (allHoles && isOperatorDisjunction(disjunction)) { auto choices = disjunction->getNestedConstraints(); for (auto *choice : choices.slice(1)) choice->setDisabled(); } } /// The common result type amongst all function overloads. Type commonResultType; auto updateCommonResultType = [&](Type choiceType) { auto markFailure = [&] { commonResultType = ErrorType::get(getASTContext()); }; auto choiceFnType = choiceType->getAs(); if (!choiceFnType) return markFailure(); // For now, don't attempt to establish a common result type when there // are type parameters. Type choiceResultType = choiceFnType->getResult(); if (choiceResultType->hasTypeParameter()) return markFailure(); // If we haven't seen a common result type yet, record what we found. if (!commonResultType) { commonResultType = choiceResultType; return; } // If we found something different, fail. if (!commonResultType->isEqual(choiceResultType)) return markFailure(); }; auto *argList = getArgumentList(getConstraintLocator(locator)); // If argument list has trailing closures and this is `init` call to // a callable type, let's not filter anything since there is a possibility // that it needs an implicit `.callAsFunction` to work. if (argList && argList->hasAnyTrailingClosures()) { if (disjunction->getLocator() ->isLastElement()) { auto choice = disjunction->getNestedConstraints()[0]->getOverloadChoice(); if (auto *decl = choice.getDeclOrNull()) { auto *dc = decl->getDeclContext(); if (auto *parent = dc->getSelfNominalTypeDecl()) { auto type = parent->getDeclaredInterfaceType(); if (type->isCallAsFunctionType(DC)) return false; } } } } // Consider each of the constraints in the disjunction. retry_after_fail: bool hasUnhandledConstraints = false; bool labelMismatch = false; auto filterResult = filterDisjunction(disjunction, /*restoreOnFail=*/shouldAttemptFixes(), [&](Constraint *constraint) { assert(constraint->getKind() == ConstraintKind::BindOverload); auto choice = constraint->getOverloadChoice(); // Determine whether the argument labels we have conflict with those of // this overload choice. if (argList) { auto args = argFnType->getParams(); SmallVector argsWithLabels; argsWithLabels.append(args.begin(), args.end()); FunctionType::relabelParams(argsWithLabels, argList); auto labelsMatch = [&](MatchCallArgumentListener &listener) { if (areConservativelyCompatibleArgumentLabels( *this, choice, argsWithLabels, listener, argList->getFirstTrailingClosureIndex())) return true; labelMismatch = true; return false; }; AllowLabelMismatches listener; // This overload has more problems than just missing/invalid labels. if (!labelsMatch(listener)) return false; // If overload did match, let's check if it needs to be disabled // in "performance" mode because it has missing labels. if (listener.hadLabelingIssues()) { // In performance mode, let's just disable the choice, // this decision could be rolled back for diagnostics. if (!shouldAttemptFixes()) return false; // Match expected vs. actual to see whether the only kind // of problem here is missing label(s). auto onlyMissingLabels = [argList](ArrayRef expectedLabels) { if (argList->size() != expectedLabels.size()) return false; for (auto i : indices(*argList)) { auto actual = argList->getLabel(i); auto expected = expectedLabels[i]; if (actual.compare(expected) != 0 && !actual.empty()) return false; } return true; }; auto replacementLabels = listener.getLabelReplacements(); // Either it's just one argument or all issues are missing labels. if (!replacementLabels || onlyMissingLabels(*replacementLabels)) { constraint->setDisabled(/*enableForDiagnostics=*/true); // Don't include this overload in "common result" computation // because it has issues. return true; } } } // Determine the type that this choice will have. Type choiceType = getEffectiveOverloadType( constraint->getLocator(), choice, /*allowMembers=*/true, constraint->getDeclContext()); if (!choiceType) { hasUnhandledConstraints = true; return true; } // If types of arguments/parameters and result lined up exactly, // let's favor this overload choice. // // Note this check ignores `ExtInfo` on purpose and only compares // types, if there are overloads that differ only in effects then // all of them are going to be considered and filtered as part of // "favored" group after forming a valid partial solution. if (auto *choiceFnType = choiceType->getAs()) { if (FunctionType::equalParams(argFnType->getParams(), choiceFnType->getParams()) && argFnType->getResult()->isEqual(choiceFnType->getResult())) constraint->setFavored(); } // Account for any optional unwrapping/binding for (unsigned i : range(numOptionalUnwraps)) { (void)i; if (Type objectType = choiceType->getOptionalObjectType()) choiceType = objectType; } // If we have a function type, we can compute a common result type. updateCommonResultType(choiceType); return true; }); switch (filterResult) { case SolutionKind::Error: if (labelMismatch && shouldAttemptFixes()) { argList = nullptr; goto retry_after_fail; } return true; case SolutionKind::Solved: case SolutionKind::Unsolved: break; } // If there was a constraint that we couldn't reason about, don't use the // results of any common-type computations. if (hasUnhandledConstraints) return false; // If we have a common result type, bind the expected result type to it. if (commonResultType && !commonResultType->is()) { if (isDebugMode()) { PrintOptions PO; PO.PrintTypesForDebugging = true; llvm::errs().indent(solverState ? solverState->getCurrentIndent() : 0) << "(common result type for $T" << fnTypeVar->getID() << " is " << commonResultType.getString(PO) << ")\n"; } // Introduction of a `Bind` constraint here could result in the disconnect // in the constraint system with unintended consequences because e.g. // in case of key path application it could disconnect one of the // components like subscript from the rest of the context. addConstraint(ConstraintKind::Equal, argFnType->getResult(), commonResultType, locator); } return false; } bool ConstraintSystem::simplifyAppliedOverloads( Constraint *disjunction, ConstraintLocatorBuilder locator) { auto choices = disjunction->getNestedConstraints(); assert(choices.size() >= 2); assert(choices.front()->getKind() == ConstraintKind::BindOverload); // If we've already bound the overload type var, bail. auto *typeVar = choices.front()->getFirstType()->getAs(); if (!typeVar || getFixedType(typeVar)) return false; // Try to find an applicable fn constraint that applies the overload choice. auto result = findConstraintThroughOptionals( typeVar, OptionalWrappingDirection::Unwrap, [&](Constraint *match, TypeVariableType *currentRep) { // Check to see if we have an applicable fn with a type var RHS that // matches the disjunction. if (match->getKind() != ConstraintKind::ApplicableFunction) return false; auto *rhsTyVar = match->getSecondType()->getAs(); return rhsTyVar && currentRep == getRepresentative(rhsTyVar); }); if (!result) return false; auto *applicableFn = result->first; auto *fnTypeVar = applicableFn->getSecondType()->castTo(); auto argFnType = applicableFn->getFirstType()->castTo(); recordAppliedDisjunction(disjunction->getLocator(), argFnType); return simplifyAppliedOverloadsImpl(disjunction, fnTypeVar, argFnType, /*numOptionalUnwraps*/ result->second, applicableFn->getLocator()); } bool ConstraintSystem::simplifyAppliedOverloads( Type fnType, FunctionType *argFnType, ConstraintLocatorBuilder locator) { // If we've already bound the function type, bail. auto *fnTypeVar = fnType->getAs(); if (!fnTypeVar || getFixedType(fnTypeVar)) return false; // Try to find a corresponding bind overload disjunction. unsigned numOptionalUnwraps = 0; auto *disjunction = getUnboundBindOverloadDisjunction(fnTypeVar, &numOptionalUnwraps); if (!disjunction) return false; recordAppliedDisjunction(disjunction->getLocator(), argFnType); return simplifyAppliedOverloadsImpl(disjunction, fnTypeVar, argFnType, numOptionalUnwraps, locator); } /// Create an implicit dot-member reference expression to be used /// as a root for injected `.callAsFunction` call. static UnresolvedDotExpr * createImplicitRootForCallAsFunction(ConstraintSystem &cs, Type refType, ArgumentList *arguments, ConstraintLocator *calleeLocator) { auto &ctx = cs.getASTContext(); auto *baseExpr = castToExpr(calleeLocator->getAnchor()); SmallVector closureLabelsScratch; // Create implicit `.callAsFunction` expression to use as an anchor // for new argument list that only has trailing closures in it. auto *implicitRef = UnresolvedDotExpr::createImplicit( ctx, baseExpr, {ctx.Id_callAsFunction}, arguments->getArgumentLabels(closureLabelsScratch)); { // Record a type of the new reference in the constraint system. cs.setType(implicitRef, refType); // Record new `.callAsFunction` in the constraint system. cs.recordImplicitCallAsFunctionRoot(calleeLocator, implicitRef); auto *implicitRefLocator = cs.getConstraintLocator( implicitRef, ConstraintLocator::ApplyArgument); cs.associateArgumentList(implicitRefLocator, arguments); } return implicitRef; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyApplicableFnConstraint( FunctionType *func1, Type type2, std::optional trailingClosureMatching, DeclContext *useDC, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto &ctx = getASTContext(); // Before stripping lvalue-ness and optional types, save the original second // type for handling `func callAsFunction` and `@dynamicCallable` // applications. This supports the following cases: // - Generating constraints for `mutating func callAsFunction`. The nominal // type (`type2`) should be an lvalue type. // - Extending `Optional` itself with `func callAsFunction` or // `@dynamicCallable` functionality. Optional types are stripped below if // `shouldAttemptFixes()` is true. auto origLValueType2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/false); // Drill down to the concrete type on the right hand side. type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true); auto desugar2 = type2->getDesugaredType(); // If a type variable representing "function type" is a hole // or it could be bound to some concrete type with a help of // a fix, let's propagate holes to the "input" type. Doing so // provides more information to upcoming argument and result matching. if (shouldAttemptFixes()) { if (auto *typeVar = type2->getAs()) { auto *locator = typeVar->getImpl().getLocator(); if (hasFixFor(locator)) { recordAnyTypeVarAsPotentialHole(func1); } } Type underlyingType = desugar2; while (auto *MT = underlyingType->getAs()) { underlyingType = MT->getInstanceType(); } underlyingType = getFixedTypeRecursive(underlyingType, flags, /*wantRValue=*/true); if (underlyingType->isPlaceholder()) { recordAnyTypeVarAsPotentialHole(func1); return SolutionKind::Solved; } } TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); SmallVector parts; auto anchor = locator.getLocatorParts(parts); bool isOperator = (isExpr(anchor) || isExpr(anchor) || isExpr(anchor)); auto hasInOut = [&]() { for (auto param : func1->getParams()) if (param.isInOut()) return true; return false; }; // Local function to form an unsolved result. auto formUnsolved = [&](bool activate = false) { if (flags.contains(TMF_GenerateConstraints)) { auto *application = Constraint::createApplicableFunction( *this, func1, type2, trailingClosureMatching, useDC, getConstraintLocator(locator)); addUnsolvedConstraint(application); if (activate) activateConstraint(application); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; // If right-hand side is a type variable, the constraint is unsolved. if (desugar2->isTypeVariableOrMember()) { return formUnsolved(); } // Strip the 'ApplyFunction' off the locator. // FIXME: Perhaps ApplyFunction can go away entirely? assert(!parts.empty() && "Nonsensical applicable-function locator"); assert(parts.back().getKind() == ConstraintLocator::ApplyFunction); assert(parts.back().getNewSummaryFlags() == 0); parts.pop_back(); ConstraintLocatorBuilder outerLocator = getConstraintLocator(anchor, parts, locator.getSummaryFlags()); // If the types are obviously equivalent, we're done. This optimization // is not valid for operators though, where an inout parameter does not // have an explicit inout argument. if (func1 == desugar2) { // Note that this could throw. recordPotentialThrowSite( PotentialThrowSite::Application, Type(desugar2), outerLocator); if (!isOperator || !hasInOut()) { recordMatchCallArgumentResult( getConstraintLocator( outerLocator.withPathElement(ConstraintLocator::ApplyArgument)), MatchCallArgumentResult::forArity(func1->getNumParams())); return SolutionKind::Solved; } } // Handle applications of types with `callAsFunction` methods. // Do this before stripping optional types below, when `shouldAttemptFixes()` // is true. if (desugar2->isCallAsFunctionType(DC)) { auto memberLoc = getConstraintLocator( locator.withPathElement(ConstraintLocator::ImplicitCallAsFunction)); // Add a `callAsFunction` member constraint, binding the member type to a // type variable. auto memberTy = createTypeVariable(memberLoc, /*options=*/0); // TODO: Revisit this if `static func callAsFunction` is to be supported. // Static member constraint requires `FunctionRefInfo::DoubleApply`. addValueMemberConstraint(origLValueType2, DeclNameRef(ctx.Id_callAsFunction), memberTy, DC, FunctionRefInfo::singleBaseNameApply(), /*outerAlternatives*/ {}, memberLoc); // Add new applicable function constraint based on the member type // variable. addApplicationConstraint(func1, memberTy, trailingClosureMatching, useDC, locator); return SolutionKind::Solved; } // Record the second type before unwrapping optionals. auto origType2 = desugar2; unsigned unwrapCount = 0; if (shouldAttemptFixes()) { // If we have an optional type, try forcing it to see if that // helps. Note that we only deal with function and metatype types // below, so there is no reason not to attempt to strip these off // immediately. while (auto objectType2 = desugar2->getOptionalObjectType()) { type2 = objectType2; desugar2 = type2->getDesugaredType(); // Track how many times we do this so that we can record a fix for each. ++unwrapCount; } } // For a function, bind the output and convert the argument to the input. if (auto func2 = dyn_cast(desugar2)) { // Note that this could throw. recordPotentialThrowSite( PotentialThrowSite::Application, Type(desugar2), outerLocator); ConstraintKind subKind = (isOperator ? ConstraintKind::OperatorArgumentConversion : ConstraintKind::ArgumentConversion); auto *argumentsLoc = getConstraintLocator( outerLocator.withPathElement(ConstraintLocator::ApplyArgument)); auto *argumentList = getArgumentList(argumentsLoc); // The argument type must be convertible to the input type. SmallVector, 2> openedExistentials; auto matchCallResult = ::matchCallArguments( *this, func2, argumentList, func1->getParams(), func2->getParams(), subKind, argumentsLoc, trailingClosureMatching, openedExistentials); switch (matchCallResult) { case SolutionKind::Error: { auto resultTy = func2->getResult(); // If this is a call that constructs a callable type with // trailing closure(s), closure(s) might not belong to // the constructor but rather to implicit `callAsFunction`, // there is no way to determine that without trying. if (resultTy->isCallAsFunctionType(DC) && argumentList->hasAnyTrailingClosures()) { auto *calleeLoc = getCalleeLocator(argumentsLoc); bool isInit = false; if (auto overload = findSelectedOverloadFor(calleeLoc)) { isInit = bool(dyn_cast_or_null( overload->choice.getDeclOrNull())); } if (!isInit) return SolutionKind::Error; auto &ctx = getASTContext(); auto numTrailing = argumentList->getNumTrailingClosures(); SmallVector newArguments( argumentList->getNonTrailingArgs()); SmallVector trailingClosures( argumentList->getTrailingClosures()); // Original argument list with all the trailing closures removed. auto *newArgumentList = ArgumentList::createParsed( ctx, argumentList->getLParenLoc(), newArguments, argumentList->getRParenLoc(), /*firstTrailingClosureIndex=*/std::nullopt); auto trailingClosureTypes = func1->getParams().take_back(numTrailing); // The original result type is going to become a result of // implicit `.callAsFunction` instead since `.callAsFunction` // is inserted between `.init` and trailing closures. auto callAsFunctionResultTy = func1->getResult(); // The implicit replacement for original result type which // represents a callable type produced by `.init` call. auto callableType = createTypeVariable(getConstraintLocator({}), /*flags=*/0); // The original application type with all the trailing closures // dropped from it and result replaced to the implicit variable. func1 = FunctionType::get(func1->getParams().drop_back(numTrailing), callableType, func1->getExtInfo()); auto matchCallResult = ::matchCallArguments( *this, func2, newArgumentList, func1->getParams(), func2->getParams(), subKind, argumentsLoc, trailingClosureMatching, openedExistentials); if (matchCallResult != SolutionKind::Solved) return SolutionKind::Error; auto *implicitCallArgumentList = ArgumentList::createImplicit(ctx, trailingClosures, /*firstTrailingClosureIndex=*/0); auto *implicitRef = createImplicitRootForCallAsFunction( *this, callAsFunctionResultTy, implicitCallArgumentList, calleeLoc); auto callAsFunctionArguments = FunctionType::get(trailingClosureTypes, callAsFunctionResultTy, FunctionType::ExtInfo()); // Form an unsolved constraint to apply trailing closures to a // callable type produced by `.init`. This constraint would become // active when `callableType` is bound. addUnsolvedConstraint(Constraint::createApplicableFunction( *this, callAsFunctionArguments, callableType, trailingClosureMatching, useDC, getConstraintLocator(implicitRef, ConstraintLocator::ApplyFunction))); break; } return SolutionKind::Error; } case SolutionKind::Unsolved: { // Only occurs when there is an ambiguity between forward scanning and // backward scanning for the unlabeled trailing closure. Create a // disjunction so that we explore both paths, and can diagnose // ambiguities later. assert(!trailingClosureMatching.has_value()); auto applyLocator = getConstraintLocator(locator); auto forwardConstraint = Constraint::createApplicableFunction( *this, func1, type2, TrailingClosureMatching::Forward, useDC, applyLocator); auto backwardConstraint = Constraint::createApplicableFunction( *this, func1, type2, TrailingClosureMatching::Backward, useDC, applyLocator); addDisjunctionConstraint({forwardConstraint, backwardConstraint}, applyLocator); break; } case SolutionKind::Solved: // Keep going. break; } // Erase all of the opened existentials. Type result2 = func2->getResult(); if (result2->hasTypeVariable() && !openedExistentials.empty()) { for (const auto &opened : openedExistentials) { auto originalTy = result2; if (auto *lvalueTy = dyn_cast(originalTy.getPointer())) { originalTy = lvalueTy->getObjectType(); } const auto erasedTy = typeEraseOpenedExistentialReference( originalTy, opened.second->getExistentialType(), opened.first, TypePosition::Covariant); if (originalTy.getPointer() != erasedTy.getPointer()) { // We currently cannot keep lvalueness if the object type changed. result2 = erasedTy; } } } // The result types are equivalent. if (matchFunctionResultTypes( func1->getResult(), result2, subflags, locator.withPathElement(ConstraintLocator::FunctionResult)) .isFailure()) return SolutionKind::Error; if (unwrapCount == 0) return SolutionKind::Solved; // Record any fixes we attempted to get to the correct solution. auto *fix = ForceOptional::create(*this, origType2, func1, getConstraintLocator(locator)); if (recordFix(fix, /*impact=*/unwrapCount)) return SolutionKind::Error; return SolutionKind::Solved; } // For a metatype, perform a construction. if (auto meta2 = dyn_cast(desugar2)) { auto instance2 = getFixedTypeRecursive(meta2->getInstanceType(), true); if (instance2->isTypeVariableOrMember()) return formUnsolved(); auto *argumentsLoc = getConstraintLocator( outerLocator.withPathElement(ConstraintLocator::ApplyArgument)); auto *argumentList = getArgumentList(argumentsLoc); assert(argumentList); // Cannot simplify construction of callable types during constraint // generation when trailing closures are present because such calls // have special trailing closure matching semantics. It's unclear // whether trailing arguments belong to `.init` or implicit // `.callAsFunction` in this case. // // Note that the constraint has to be activate so that solver attempts // once constraint generation is done. if (getPhase() == ConstraintSystemPhase::ConstraintGeneration && argumentList->hasAnyTrailingClosures() && instance2->isCallAsFunctionType(DC)) { return formUnsolved(/*activate=*/true); } // Construct the instance from the input arguments. auto simplified = simplifyConstructionConstraint( instance2, func1, subflags, useDC, FunctionRefInfo::singleBaseNameApply(), getConstraintLocator(outerLocator)); // Record any fixes we attempted to get to the correct solution. if (simplified == SolutionKind::Solved) { if (unwrapCount == 0) return SolutionKind::Solved; auto *fix = ForceOptional::create(*this, origType2, func1, getConstraintLocator(locator)); if (recordFix(fix, /*impact=*/unwrapCount)) return SolutionKind::Error; } return simplified; } // Handle applications of @dynamicCallable types. auto result = simplifyDynamicCallableApplicableFnConstraint( func1, origType2, subflags, locator); if (shouldAttemptFixes() && result == SolutionKind::Error) { // Skip this fix if the type is not yet resolved or // it's a function type/metatype which points to argument mismatches. if (desugar2->is() || desugar2->is() || desugar2->is()) return SolutionKind::Error; // If there are any type variables associated with arguments/result // they have to be marked as "holes". recordAnyTypeVarAsPotentialHole(func1); if (desugar2->isPlaceholder()) return SolutionKind::Solved; auto *fix = RemoveInvalidCall::create(*this, getConstraintLocator(locator)); // Let's make this fix as high impact so if there is a function or member // overload with e.g. argument-to-parameter type mismatches it would take // a higher priority. return recordFix(fix, /*impact=*/3) ? SolutionKind::Error : SolutionKind::Solved; } return result; } /// Looks up and returns the @dynamicCallable required methods (if they exist) /// implemented by a type. static llvm::DenseSet lookupDynamicCallableMethods(NominalTypeDecl *decl, ConstraintSystem &CS, const ConstraintLocatorBuilder &locator, Identifier argumentName, bool hasKeywordArgs) { auto &ctx = CS.getASTContext(); // The generic arguments don't matter because we only want the member decls, // not concrete overload choices (we form those later when adding the overload // set). We map into context here to avoid an OverloadChoice assertion for an // interface type base. // TODO: We really ought to separate out the actual lookup part of // `performMemberLookup` from the choice construction. That would allow us to // requestify the lookup of dynamicCallable members on a per-decl basis, and // map them onto viable and unviable choices onto a given base type. auto type = decl->getDeclaredTypeInContext(); DeclNameRef methodName({ ctx, ctx.Id_dynamicallyCall, { argumentName } }); auto matches = CS.performMemberLookup( ConstraintKind::ValueMember, methodName, type, FunctionRefInfo::singleBaseNameApply(), CS.getConstraintLocator(locator), /*includeInaccessibleMembers*/ false); // Filter valid candidates. auto candidates = matches.ViableCandidates; auto filter = [&](OverloadChoice choice) { auto cand = cast(choice.getDecl()); return !isValidDynamicCallableMethod(cand, hasKeywordArgs); }; candidates.erase( std::remove_if(candidates.begin(), candidates.end(), filter), candidates.end()); llvm::DenseSet methods; for (auto candidate : candidates) methods.insert(cast(candidate.getDecl())); return methods; } /// Looks up and returns the @dynamicCallable required methods (if they exist) /// implemented by a given nominal type decl. static DynamicCallableMethods lookupDynamicCallableMethods(NominalTypeDecl *decl, ConstraintSystem &CS, const ConstraintLocatorBuilder &locator) { auto it = CS.DynamicCallableCache.find(decl); if (it != CS.DynamicCallableCache.end()) return it->second; // The decl must have @dynamicCallable. auto &ctx = CS.getASTContext(); HasDynamicCallableAttributeRequest req(decl); if (!evaluateOrDefault(ctx.evaluator, req, false)) return DynamicCallableMethods(); DynamicCallableMethods methods; methods.argumentsMethods = lookupDynamicCallableMethods(decl, CS, locator, ctx.Id_withArguments, /*hasKeywordArgs*/ false); methods.keywordArgumentsMethods = lookupDynamicCallableMethods(decl, CS, locator, ctx.Id_withKeywordArguments, /*hasKeywordArgs*/ true); CS.DynamicCallableCache[decl] = methods; return methods; } /// Returns the @dynamicCallable required methods (if they exist) implemented /// by a type. static DynamicCallableMethods getDynamicCallableMethods(Type type, ConstraintSystem &CS, const ConstraintLocatorBuilder &locator) { SmallVector decls; namelookup::tryExtractDirectlyReferencedNominalTypes(type, decls); DynamicCallableMethods result; for (auto *decl : decls) result.addMethods(lookupDynamicCallableMethods(decl, CS, locator)); return result; } // TODO: Refactor/simplify this function. // - It should perform less duplicate work with its caller // `ConstraintSystem::simplifyApplicableFnConstraint`. // - It should generate a member constraint instead of manually forming an // overload set for `func dynamicallyCall` candidates. // - It should support `mutating func dynamicallyCall`. This should fall out of // using member constraints with an lvalue base type. ConstraintSystem::SolutionKind ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto &ctx = getASTContext(); // By construction, the left hand side is a function type: $T1 -> $T2. assert(type1->is()); // Drill down to the concrete type on the right hand side. type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true); auto desugar2 = type2->getDesugaredType(); TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); // If the types are obviously equivalent, we're done. if (type1.getPointer() == desugar2) return SolutionKind::Solved; // Local function to form an unsolved result. auto formUnsolved = [&] { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( Constraint::create(*this, ConstraintKind::DynamicCallableApplicableFunction, type1, type2, getConstraintLocator(locator))); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; // If right-hand side is a type variable, the constraint is unsolved. if (desugar2->isTypeVariableOrMember()) return formUnsolved(); // If right-hand side is a function type, it must be a valid // `dynamicallyCall` method type. Bind the output and convert the argument // to the input. auto func1 = type1->castTo(); if (auto func2 = dyn_cast(desugar2)) { // The argument type must be convertible to the input type. assert(func1->getParams().size() == 1 && func2->getParams().size() == 1 && "Expected `dynamicallyCall` method with one parameter"); assert((func2->getParams()[0].getLabel() == ctx.Id_withArguments || func2->getParams()[0].getLabel() == ctx.Id_withKeywordArguments) && "Expected 'dynamicallyCall' method argument label 'withArguments' " "or 'withKeywordArguments'"); if (matchTypes(func1->getParams()[0].getPlainType(), func2->getParams()[0].getPlainType(), ConstraintKind::ArgumentConversion, subflags, locator.withPathElement( ConstraintLocator::ApplyArgument)).isFailure()) return SolutionKind::Error; // The result types are equivalent. if (matchFunctionResultTypes( func1->getResult(), func2->getResult(), subflags, locator.withPathElement(ConstraintLocator::FunctionResult)) .isFailure()) return SolutionKind::Error; return SolutionKind::Solved; } // If the right-hand side is not a function type, it must be a valid // @dynamicCallable type. Attempt to get valid `dynamicallyCall` methods. auto methods = getDynamicCallableMethods(desugar2, *this, locator); if (!methods.isValid()) return SolutionKind::Error; // Determine whether to call a `withArguments` method or a // `withKeywordArguments` method. bool useKwargsMethod = methods.argumentsMethods.empty(); useKwargsMethod |= llvm::any_of( func1->getParams(), [](AnyFunctionType::Param p) { return p.hasLabel(); }); auto candidates = useKwargsMethod ? methods.keywordArgumentsMethods : methods.argumentsMethods; // Create a type variable for the `dynamicallyCall` method. auto loc = getConstraintLocator(locator); auto tv = createTypeVariable(loc, TVO_CanBindToLValue | TVO_CanBindToNoEscape); // Record the 'dynamicallyCall` method overload set. SmallVector choices; for (auto candidate : candidates) { if (candidate->isInvalid()) continue; choices.push_back(OverloadChoice(type2, candidate, FunctionRefInfo::singleBaseNameApply())); } if (choices.empty()) { if (!shouldAttemptFixes()) return SolutionKind::Error; // TODO(diagnostics): This is not going to be necessary once // `@dynamicCallable` uses existing `member` machinery. auto argLabel = useKwargsMethod ? ctx.Id_withKeywordArguments : ctx.Id_withArguments; DeclNameRef memberName({ ctx, ctx.Id_dynamicallyCall, {argLabel} }); auto *fix = DefineMemberBasedOnUse::create( *this, desugar2, memberName, /*alreadyDiagnosed=*/false, getConstraintLocator(loc, ConstraintLocator::DynamicCallable)); if (recordFix(fix)) return SolutionKind::Error; recordPotentialHole(tv); recordAnyTypeVarAsPotentialHole(func1); return SolutionKind::Solved; } addOverloadSet(tv, choices, DC, loc); // Create a type variable for the argument to the `dynamicallyCall` method. auto tvParam = createTypeVariable(loc, TVO_CanBindToNoEscape); AnyFunctionType *funcType = FunctionType::get({ AnyFunctionType::Param(tvParam) }, func1->getResult()); addConstraint(ConstraintKind::DynamicCallableApplicableFunction, funcType, tv, locator); // Get argument type for the `dynamicallyCall` method. Type argumentType; if (!useKwargsMethod) { auto arrayLitProto = ctx.getProtocol(KnownProtocolKind::ExpressibleByArrayLiteral); addConstraint(ConstraintKind::ConformsTo, tvParam, arrayLitProto->getDeclaredInterfaceType(), locator); auto elementAssocType = arrayLitProto->getAssociatedType( ctx.Id_ArrayLiteralElement); argumentType = DependentMemberType::get(tvParam, elementAssocType); } else { auto dictLitProto = ctx.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral); addConstraint(ConstraintKind::ConformsTo, tvParam, dictLitProto->getDeclaredInterfaceType(), locator); auto valueAssocType = dictLitProto->getAssociatedType(ctx.Id_Value); argumentType = DependentMemberType::get(tvParam, valueAssocType); } // Argument type can default to `Any`. addConstraint(ConstraintKind::Defaultable, argumentType, ctx.getAnyExistentialType(), locator); auto *baseArgLoc = getConstraintLocator( loc->getAnchor(), {ConstraintLocator::DynamicCallable, ConstraintLocator::ApplyArgument}, /*summaryFlags=*/0); // All dynamic call parameter types must be convertible to the argument type. for (auto i : indices(func1->getParams())) { auto param = func1->getParams()[i]; auto paramType = param.getPlainType(); addConstraint( ConstraintKind::ArgumentConversion, paramType, argumentType, getConstraintLocator(baseArgLoc, LocatorPathElt::ApplyArgToParam( i, 0, param.getParameterFlags()))); } return SolutionKind::Solved; } static bool hasUnresolvedPackVars(Type type) { // We can't compute a reduced shape if the input type still // contains type variables that might bind to pack archetypes // or pack expansions. SmallPtrSet typeVars; type->getTypeVariables(typeVars); return llvm::any_of(typeVars, [](const TypeVariableType *typeVar) { return typeVar->getImpl().canBindToPack() || typeVar->getImpl().isPackExpansion(); }); } ConstraintSystem::SolutionKind ConstraintSystem::simplifyShapeOfConstraint( Type shapeTy, Type packTy, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { // Recursively replace all type variables with fixed bindings if // possible. packTy = simplifyType(packTy, flags); auto formUnsolved = [&]() { // If we're supposed to generate constraints, do so. if (flags.contains(TMF_GenerateConstraints)) { auto *shapeOf = Constraint::create( *this, ConstraintKind::ShapeOf, shapeTy, packTy, getConstraintLocator(locator)); addUnsolvedConstraint(shapeOf); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; // Don't try computing the shape of a type variable. if (packTy->isTypeVariableOrMember()) return formUnsolved(); // We can't compute a reduced shape if the input type still // contains type variables that might bind to pack archetypes // or pack expansions. SmallPtrSet typeVars; packTy->getTypeVariables(typeVars); for (auto *typeVar : typeVars) { if (typeVar->getImpl().canBindToPack() || typeVar->getImpl().isPackExpansion()) return formUnsolved(); } if (packTy->hasPlaceholder()) { if (!shouldAttemptFixes()) return SolutionKind::Error; recordTypeVariablesAsHoles(shapeTy); return SolutionKind::Solved; } if (isSingleUnlabeledPackExpansionTuple(packTy)) { auto *packVar = addMaterializePackExpansionConstraint(packTy, locator); addConstraint(ConstraintKind::ShapeOf, shapeTy, packVar, locator); return SolutionKind::Solved; } // Map element archetypes to the pack context to check for equality. if (packTy->hasElementArchetype()) { auto *packEnv = DC->getGenericEnvironmentOfContext(); packTy = packEnv->mapElementTypeIntoPackContext(packTy); } auto shape = packTy->getReducedShape(); addConstraint(ConstraintKind::Bind, shapeTy, shape, locator); return SolutionKind::Solved; } ConstraintSystem::SolutionKind ConstraintSystem::simplifySameShapeConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { type1 = simplifyType(type1); type2 = simplifyType(type2); auto formUnsolved = [&]() { // If we're supposed to generate constraints, do so. if (flags.contains(TMF_GenerateConstraints)) { auto *sameShape = Constraint::create(*this, ConstraintKind::SameShape, type1, type2, getConstraintLocator(locator)); addUnsolvedConstraint(sameShape); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; if (hasUnresolvedPackVars(type1) || hasUnresolvedPackVars(type2)) return formUnsolved(); auto shape1 = type1->getReducedShape(); auto shape2 = type2->getReducedShape(); if (shape1->isEqual(shape2)) return SolutionKind::Solved; if (shouldAttemptFixes()) { // If there are placeholders involved shape mismatches are most // likely just a symptom of some other issue i.e. type mismatch. if (type1->hasPlaceholder() || type2->hasPlaceholder()) return SolutionKind::Solved; auto recordShapeFix = [&](ConstraintFix *fix, unsigned impact) -> SolutionKind { return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved; }; auto recordShapeMismatchFix = [&]() -> SolutionKind { unsigned impact = 1; if (locator.endsWith()) impact = assessRequirementFailureImpact(*this, shape1, locator); return recordShapeFix( SkipSameShapeRequirement::create(*this, type1, type2, getConstraintLocator(locator)), impact); }; // Let's check whether we can produce a tailored fix for argument/parameter // mismatches. if (locator.endsWith()) { SmallVector path; auto anchor = locator.getLocatorParts(path); // Drop `PackShape` path.pop_back(); // Tailed diagnostics for argument/parameter mismatches - there // are either missing or extra arguments. if (path.size() > 0 && path[path.size() - 1].is()) { auto &ctx = getASTContext(); auto *loc = getConstraintLocator(anchor, path); auto argLoc = loc->castLastElementTo(); if (type1->is() && type2->is()) return recordShapeMismatchFix(); auto numArgs = (shape1->is() ? shape1->castTo()->getNumElements() : 1); auto numParams = (shape2->is() ? shape2->castTo()->getNumElements() : 1); // Tailed diagnostic to explode tuples. // FIXME: This is very similar to // 'cannot_convert_single_tuple_into_multiple_arguments'; can we emit // both of these in the same place? if (numArgs == 1) { if (type1->is() && numParams >= 1) { return recordShapeFix( DestructureTupleToMatchPackExpansionParameter::create( *this, (type2->is() ? type2->castTo() : PackType::getSingletonPackExpansion(type2)), loc), /*impact=*/2 * numParams); } } // Drops `ApplyArgToParam` and left with `ApplyArgument`. path.pop_back(); auto *argListLoc = getConstraintLocator(anchor, path); // Missing arguments. if (numParams > numArgs) { SmallVector synthesizedArgs; for (unsigned i = 0, n = numParams - numArgs; i != n; ++i) { auto eltTy = shape2->castTo()->getElementType(i); synthesizedArgs.push_back(SynthesizedArg{ argLoc.getParamIdx(), AnyFunctionType::Param(eltTy)}); } return recordShapeFix( AddMissingArguments::create(*this, synthesizedArgs, argListLoc), /*impact=*/2 * synthesizedArgs.size()); } else { auto argIdx = argLoc.getArgIdx() + numParams; SmallVector, 4> extraneousArgs; for (unsigned i = 0, n = numArgs - numParams; i != n; ++i) { extraneousArgs.push_back( {argIdx + i, AnyFunctionType::Param(ctx.TheEmptyTupleType)}); } auto overload = findSelectedOverloadFor(getCalleeLocator(argListLoc)); if (!overload) return SolutionKind::Error; return recordShapeFix( RemoveExtraneousArguments::create( *this, overload->openedType->castTo(), extraneousArgs, argListLoc), /*impact=*/2 * extraneousArgs.size()); } } } return recordShapeMismatchFix(); } return SolutionKind::Error; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyMaterializePackExpansionConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto formUnsolved = [&]() { // If we're supposed to generate constraints, do so. if (flags.contains(TMF_GenerateConstraints)) { auto *explictGenericArgs = Constraint::create(*this, ConstraintKind::MaterializePackExpansion, type1, type2, getConstraintLocator(locator)); addUnsolvedConstraint(explictGenericArgs); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; type1 = simplifyType(type1); if (type1->hasTypeVariable()) { return formUnsolved(); } if (auto patternType = getPatternTypeOfSingleUnlabeledPackExpansionTuple(type1)) { addConstraint(ConstraintKind::Equal, patternType, type2, locator); return SolutionKind::Solved; } return SolutionKind::Error; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyExplicitGenericArgumentsConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto formUnsolved = [&]() { // If we're supposed to generate constraints, do so. if (flags.contains(TMF_GenerateConstraints)) { auto *explictGenericArgs = Constraint::create(*this, ConstraintKind::ExplicitGenericArguments, type1, type2, getConstraintLocator(locator)); addUnsolvedConstraint(explictGenericArgs); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; // Bail out if we haven't selected an overload yet. auto simplifiedBoundType = simplifyType(type1, flags); if (simplifiedBoundType->isPlaceholder()) return SolutionKind::Solved; if (simplifiedBoundType->isTypeVariableOrMember()) return formUnsolved(); std::function getGenericParams = [&](ValueDecl *decl) -> GenericParamList * { auto genericContext = decl->getAsGenericContext(); if (!genericContext) return nullptr; auto genericParams = genericContext->getGenericParams(); if (!genericParams) { // If declaration is a non-generic typealias, let's point // to the underlying generic declaration. if (auto *TA = dyn_cast(decl)) { if (auto *UGT = TA->getUnderlyingType()->getAs()) return getGenericParams(UGT->getDecl()); } } return genericParams; }; auto fixInvalidSpecialization = [&](ValueDecl *decl) -> SolutionKind { if (isa(decl)) { return recordFix(AllowFunctionSpecialization::create( *this, decl, getConstraintLocator(locator))) ? SolutionKind::Error : SolutionKind::Solved; } // Allow concrete macros to have specializations with just a warning. return recordFix(AllowConcreteTypeSpecialization::create( *this, type1, decl, getConstraintLocator(locator), isa(decl) ? FixBehavior::DowngradeToWarning : FixBehavior::Error)) ? SolutionKind::Error : SolutionKind::Solved; }; ValueDecl *decl; SmallVector openedTypes; if (auto *bound = dyn_cast(type1.getPointer())) { decl = bound->getDecl(); for (auto argType : bound->getDirectGenericArgs()) { auto *typeVar = argType->getAs(); if (!typeVar) return SolutionKind::Error; auto *genericParam = typeVar->getImpl().getGenericParameter(); openedTypes.push_back({genericParam, typeVar}); } } else if (locator.directlyAt()) { auto *BGT = type1->getAs(); if (!BGT) return SolutionKind::Error; decl = BGT->getDecl(); auto genericParams = BGT->getDecl()->getInnermostGenericParamTypes(); if (genericParams.size() != BGT->getGenericArgs().size()) return SolutionKind::Error; for (unsigned i = 0, n = genericParams.size(); i != n; ++i) { auto argType = BGT->getGenericArgs()[i]; if (auto *typeVar = argType->getAs()) { openedTypes.push_back({genericParams[i], typeVar}); } else { // If we have a concrete substitution then we need to create // a new type variable to be able to add it to the list as-if // it is opened generic parameter type. auto *GP = genericParams[i]; unsigned options = TVO_CanBindToNoEscape; if (GP->isParameterPack()) options |= TVO_CanBindToPack; auto *argVar = createTypeVariable( getConstraintLocator(locator, LocatorPathElt::GenericArgument(i)), options); addConstraint(ConstraintKind::Bind, argVar, argType, locator); openedTypes.push_back({GP, argVar}); } } } else { // If the overload hasn't been resolved, we can't simplify this constraint. auto overloadLocator = getCalleeLocator(getConstraintLocator(locator)); // If there was a problem resolving specialization expression // it would be diagnosted as invalid AST node. if (overloadLocator->directlyAt()) { return shouldAttemptFixes() ? SolutionKind::Error : SolutionKind::Solved; } auto selectedOverload = findSelectedOverloadFor(overloadLocator); if (!selectedOverload) return formUnsolved(); auto overloadChoice = selectedOverload->choice; if (!overloadChoice.isDecl()) { return SolutionKind::Error; } decl = overloadChoice.getDecl(); auto openedOverloadTypes = getOpenedTypes(overloadLocator); // Attempting to specialize a non-generic declaration. if (openedOverloadTypes.empty()) { // Note that this is unconditional because the fix is // downgraded to a warning in swift language modes < 6. return fixInvalidSpecialization(decl); } auto genericParams = getGenericParams(decl); if (genericParams) { for (auto gp : *genericParams) { auto found = find_if(openedOverloadTypes, [&](auto entry) { return entry.first->getDepth() == gp->getDepth() && entry.first->getIndex() == gp->getIndex(); }); assert(found != openedOverloadTypes.end()); openedTypes.push_back(*found); } } } auto genericParams = getGenericParams(decl); if (!decl->getAsGenericContext() || !genericParams) return fixInvalidSpecialization(decl); // Map the generic parameters we have over to their opened types. bool hasParameterPack = false; SmallVector openedGenericParams; auto genericParamDepth = genericParams->getParams()[0]->getDepth(); for (const auto &openedType : openedTypes) { if (openedType.first->getDepth() == genericParamDepth) { // A generic argument list containing pack references expects // those packs to be wrapped in pack expansion types. If this // opened type represents the generic argument for a parameter // pack, wrap generate the appropriate shape constraints and // add a pack expansion to the argument list. if (openedType.first->isParameterPack()) { auto patternType = openedType.second; auto *shapeLoc = getConstraintLocator( locator.withPathElement(ConstraintLocator::PackShape)); auto *shapeType = createTypeVariable(shapeLoc, TVO_CanBindToPack | TVO_CanBindToHole); addConstraint(ConstraintKind::ShapeOf, shapeType, patternType, shapeLoc); auto *expansion = PackExpansionType::get(patternType, shapeType); openedGenericParams.push_back(expansion); hasParameterPack = true; } else { openedGenericParams.push_back(Type(openedType.second)); } } } // FIXME: We could support explicit function specialization. if (openedGenericParams.empty() || (isa(decl) && !hasParameterPack)) { return recordFix(AllowFunctionSpecialization::create( *this, decl, getConstraintLocator(locator))) ? SolutionKind::Error : SolutionKind::Solved; } assert(openedGenericParams.size() == genericParams->size()); // Match the opened generic parameters to the specialized arguments. auto specializedArgs = type2->castTo()->getElementTypes(); PackMatcher matcher(openedGenericParams, specializedArgs, getASTContext(), isPackExpansionType); if (matcher.match()) { if (!shouldAttemptFixes()) return SolutionKind::Error; auto *fix = IgnoreGenericSpecializationArityMismatch::create( *this, decl, openedGenericParams.size(), specializedArgs.size(), hasParameterPack, getConstraintLocator(locator)); return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } // Bind the opened generic parameters to the specialization arguments. for (const auto &pair : matcher.pairs) { addConstraint( ConstraintKind::Bind, pair.lhs, pair.rhs, getConstraintLocator( locator, LocatorPathElt::GenericArgument(pair.lhsIdx))); } return SolutionKind::Solved; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyLValueObjectConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { auto lvalueTy = simplifyType(type1); auto formUnsolved = [&]() { // If we're supposed to generate constraints, do so. if (flags.contains(TMF_GenerateConstraints)) { auto *lvalueObject = Constraint::create(*this, ConstraintKind::LValueObject, type1, type2, getConstraintLocator(locator)); addUnsolvedConstraint(lvalueObject); return SolutionKind::Solved; } return SolutionKind::Unsolved; }; auto isOrCanBeLValueType = [](Type type) { if (auto *typeVar = type->getAs()) { return typeVar->getImpl().canBindToLValue(); } return type->is(); }; if (lvalueTy->isPlaceholder()) { if (!shouldAttemptFixes()) return SolutionKind::Error; recordAnyTypeVarAsPotentialHole(type2); return SolutionKind::Solved; } if (!isOrCanBeLValueType(lvalueTy)) { if (!shouldAttemptFixes()) return SolutionKind::Error; auto *fixLoc = getConstraintLocator(locator); if (recordFix(TreatRValueAsLValue::create(*this, fixLoc), TreatRValueAsLValue::assessImpact(*this, fixLoc))) return SolutionKind::Error; lvalueTy = LValueType::get(lvalueTy); } if (lvalueTy->isTypeVariableOrMember()) return formUnsolved(); // TODO: This operation deserves its own locator just like OptionalObject. addConstraint(ConstraintKind::Equal, lvalueTy->castTo()->getObjectType(), type2, getConstraintLocator(locator)); return SolutionKind::Solved; } static llvm::PointerIntPair getBaseTypeForPointer(TypeBase *type) { unsigned unwrapCount = 0; while (auto objectTy = type->getOptionalObjectType()) { type = objectTy.getPointer(); ++unwrapCount; } auto pointeeTy = type->getAnyPointerElementType(); assert(pointeeTy); return {pointeeTy, unwrapCount}; } void ConstraintSystem::addRestrictedConstraint( ConstraintKind kind, ConversionRestrictionKind restriction, Type first, Type second, ConstraintLocatorBuilder locator) { (void)simplifyRestrictedConstraint(restriction, first, second, kind, TMF_GenerateConstraints, locator); } /// Given that we have a conversion constraint between two types, and /// that the given constraint-reduction rule applies between them at /// the top level, apply it and generate any necessary recursive /// constraints. ConstraintSystem::SolutionKind ConstraintSystem::simplifyRestrictedConstraintImpl( ConversionRestrictionKind restriction, Type type1, Type type2, ConstraintKind matchKind, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { assert(!type1->isTypeVariableOrMember() && !type2->isTypeVariableOrMember()); // Add to the score based on context. auto addContextualScore = [&] { // Okay, we need to perform one or more conversions. If this // conversion will cause a function conversion, score it as worse. // This induces conversions to occur within closures instead of // outside of them wherever possible. if (locator.isFunctionConversion()) { // This conversion exists only to check adjustments in the member // type, so the fact that adjustments also cause a function conversion // is unrelated. if (locator.isForExistentialMemberAccessConversion()) return; increaseScore(SK_FunctionConversion, locator); } }; TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); auto matchPointerBaseTypes = [&](llvm::PointerIntPair baseType1, llvm::PointerIntPair baseType2) -> SolutionKind { if (restriction != ConversionRestrictionKind::PointerToPointer) increaseScore(ScoreKind::SK_ValueToPointerConversion, locator); auto result = matchTypes(baseType1.getPointer(), baseType2.getPointer(), ConstraintKind::BindToPointerType, subflags, locator); if (!(result.isFailure() && shouldAttemptFixes())) return result; BoundGenericType *ptr1 = nullptr; BoundGenericType *ptr2 = nullptr; switch (restriction) { case ConversionRestrictionKind::ArrayToPointer: case ConversionRestrictionKind::InoutToPointer: { ptr2 = type2->lookThroughAllOptionalTypes()->castTo(); ptr1 = BoundGenericType::get(ptr2->getDecl(), ptr2->getParent(), {baseType1.getPointer()}); break; } case ConversionRestrictionKind::PointerToPointer: // Original types could be wrapped into a different number of optional. ptr1 = type1->lookThroughAllOptionalTypes()->castTo(); ptr2 = type2->lookThroughAllOptionalTypes()->castTo(); break; default: return SolutionKind::Error; } auto *fix = GenericArgumentsMismatch::create(*this, ptr1, ptr2, {0}, getConstraintLocator(locator)); // Treat this as a contextual type mismatch. unsigned baseImpact = 2; // It's possible to implicitly promote pointer into an optional // before matching base types if other side is an optional, so // score needs to account for number of such promotions. int optionalWraps = baseType2.getInt() - baseType1.getInt(); return recordFix(fix, baseImpact + std::abs(optionalWraps)) ? SolutionKind::Error : SolutionKind::Solved; }; auto fixContextualFailure = [&](Type fromType, Type toType, ConstraintLocatorBuilder locator) -> bool { auto *loc = getConstraintLocator(locator); // Since this is a contextual type mismatch, let's start from higher // impact than regular fix to avoid ambiguities. auto impact = 2; if (loc->isForAssignment() || loc->isForCoercion() || loc->isForContextualType() || loc->isLastElement() || loc->isForOptionalTry()) { if (restriction == ConversionRestrictionKind::Superclass) { if (auto *fix = CoerceToCheckedCast::attempt( *this, fromType, toType, /*useConditionalCast*/ false, loc)) return !recordFix(fix, impact); } // We already have a fix for this locator indicating a // tuple mismatch. if (hasFixFor(loc, FixKind::AllowTupleTypeMismatch)) return true; if (restriction == ConversionRestrictionKind::ValueToOptional) { // If this is an optional injection we can drop optional from // "to" type since it's not significant for the diagnostic. toType = toType->getOptionalObjectType(); } ConstraintFix *fix = nullptr; if (loc->isLastElement()) { fix = AllowArgumentMismatch::create(*this, fromType, toType, loc); } else if (loc->isForAssignment()) { fix = IgnoreAssignmentDestinationType::create(*this, fromType, toType, loc); } else { fix = ContextualMismatch::create(*this, fromType, toType, loc); } assert(fix); return !recordFix(fix, impact); } return false; }; switch (restriction) { // for $< in { <, (T_i...) $< (U_i...) case ConversionRestrictionKind::DeepEquality: return matchDeepEqualityTypes(type1, type2, locator); case ConversionRestrictionKind::Superclass: { addContextualScore(); auto result = matchSuperclassTypes(type1, type2, subflags, locator); if (!(shouldAttemptFixes() && result.isFailure())) return result; return fixContextualFailure(type1, type2, locator) ? getTypeMatchSuccess() : getTypeMatchFailure(locator); } // for $< in { <, T $< protocol case ConversionRestrictionKind::Existential: addContextualScore(); return matchExistentialTypes(type1, type2, ConstraintKind::Subtype, subflags, locator); // for $< in { <, T.Protocol $< Q.Type // for P protocol, Q protocol, // P $< Q ===> P.Type $< Q.Type case ConversionRestrictionKind::MetatypeToExistentialMetatype: { addContextualScore(); auto instanceTy1 = type1->getMetatypeInstanceType(); auto instanceTy2 = type2->getMetatypeInstanceType(); auto result = matchExistentialTypes( instanceTy1, instanceTy2, ConstraintKind::ConformsTo, subflags, locator.withPathElement(ConstraintLocator::InstanceType)); if (!(shouldAttemptFixes() && result.isFailure())) return result; return fixContextualFailure(type1, type2, locator) ? getTypeMatchSuccess() : getTypeMatchFailure(locator); } // for $< in { <, (P & C).Type $< D.Type case ConversionRestrictionKind::ExistentialMetatypeToMetatype: { addContextualScore(); auto instance1 = type1->castTo()->getInstanceType(); auto instance2 = type2->castTo()->getInstanceType(); auto superclass1 = instance1->getSuperclass(); if (!superclass1) return SolutionKind::Error; auto result = matchTypes(superclass1, instance2, ConstraintKind::Subtype, subflags, locator.withPathElement(ConstraintLocator::InstanceType)); if (!(shouldAttemptFixes() && result.isFailure())) return result; return fixContextualFailure(type1, type2, locator) ? getTypeMatchSuccess() : getTypeMatchFailure(locator); } // for $< in { <, T $< U? case ConversionRestrictionKind::ValueToOptional: { addContextualScore(); increaseScore(SK_ValueToOptional, locator); assert(matchKind >= ConstraintKind::Subtype); if (auto generic2 = type2->getAs()) { if (generic2->getDecl()->isOptionalDecl()) { auto result = matchTypes( type1, generic2->getGenericArgs()[0], matchKind, subflags, locator.withPathElement(ConstraintLocator::OptionalInjection)); if (!(shouldAttemptFixes() && result.isFailure())) return result; } } return shouldAttemptFixes() && fixContextualFailure(type1, type2, locator) ? SolutionKind::Solved : SolutionKind::Error; } // for $< in { <, T? $< U? // T $< U ===> T! $< U! // T $< U ===> T! $< U? // also: // T T? = ConstraintKind::Subtype); if (auto generic1 = type1->getAs()) { if (auto generic2 = type2->getAs()) { if (generic1->getDecl()->isOptionalDecl() && generic2->getDecl()->isOptionalDecl()) { auto result = matchTypes( generic1->getGenericArgs()[0], generic2->getGenericArgs()[0], matchKind, subflags, locator.withPathElement(LocatorPathElt::GenericArgument(0))); if (!(shouldAttemptFixes() && result.isFailure())) return result; } } } return shouldAttemptFixes() && fixContextualFailure(type1, type2, locator) ? SolutionKind::Solved : SolutionKind::Error; } case ConversionRestrictionKind::ClassMetatypeToAnyObject: case ConversionRestrictionKind::ExistentialMetatypeToAnyObject: case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass: { // Nothing more to solve. addContextualScore(); return SolutionKind::Solved; } // T

T[] case ConversionRestrictionKind::ArrayToPointer: { addContextualScore(); // Unwrap an inout type. auto obj1 = type1->getInOutObjectType(); obj1 = getFixedTypeRecursive(obj1, false); auto t2 = type2->getDesugaredType(); auto baseType1 = getFixedTypeRecursive(obj1->getArrayElementType(), false); auto ptr2 = getBaseTypeForPointer(t2); increaseScore(SK_ValueToOptional, locator, ptr2.getInt()); return matchPointerBaseTypes({baseType1, 0}, ptr2); } // String ===> UnsafePointer<[U]Int8> case ConversionRestrictionKind::StringToPointer: { addContextualScore(); auto ptr2 = getBaseTypeForPointer(type2->getDesugaredType()); increaseScore(SK_ValueToOptional, locator, ptr2.getInt()); // The pointer element type must be void or a byte-sized type. // TODO: Handle different encodings based on pointer element type, such as // UTF16 for [U]Int16 or UTF32 for [U]Int32. For now we only interop with // Int8 pointers using UTF8 encoding. auto baseType2 = getFixedTypeRecursive(ptr2.getPointer(), false); // If we haven't resolved the element type, generate constraints. if (baseType2->isTypeVariableOrMember()) { if (flags.contains(TMF_GenerateConstraints)) { increaseScore(ScoreKind::SK_ValueToPointerConversion, locator); auto &ctx = getASTContext(); auto int8Con = Constraint::create(*this, ConstraintKind::Bind, baseType2, ctx.getInt8Type(), getConstraintLocator(locator)); auto uint8Con = Constraint::create(*this, ConstraintKind::Bind, baseType2, ctx.getUInt8Type(), getConstraintLocator(locator)); auto voidCon = Constraint::create(*this, ConstraintKind::Bind, baseType2, ctx.TheEmptyTupleType, getConstraintLocator(locator)); Constraint *disjunctionChoices[] = {int8Con, uint8Con, voidCon}; addDisjunctionConstraint(disjunctionChoices, locator); return SolutionKind::Solved; } return SolutionKind::Unsolved; } if (!isStringCompatiblePointerBaseType(getASTContext(), baseType2)) { return SolutionKind::Error; } increaseScore(ScoreKind::SK_ValueToPointerConversion, locator); return SolutionKind::Solved; } // T

inout T case ConversionRestrictionKind::InoutToPointer: { addContextualScore(); auto t2 = type2->getDesugaredType(); auto baseType1 = type1->getInOutObjectType(); auto ptr2 = getBaseTypeForPointer(t2); increaseScore(SK_ValueToOptional, locator, ptr2.getInt()); return matchPointerBaseTypes({baseType1, 0}, ptr2); } // T

UnsafeMutablePointer case ConversionRestrictionKind::PointerToPointer: { auto t1 = type1->getDesugaredType(); auto t2 = type2->getDesugaredType(); auto ptr1 = getBaseTypeForPointer(t1); auto ptr2 = getBaseTypeForPointer(t2); return matchPointerBaseTypes(ptr1, ptr2); } case ConversionRestrictionKind::PointerToCPointer: return simplifyPointerToCPointerRestriction(type1, type2, flags, locator); case ConversionRestrictionKind::ArrayToCPointer: { auto ptr2 = type2->getDesugaredType()->lookThroughAllOptionalTypes(); PointerTypeKind pointerKind; auto cPtr = ptr2->getAnyPointerElementType(pointerKind); // If the parameter is a raw pointer or its element type is not a // supported (un-)signed integer it implies a regular ArrayToPointer // conversion. if (isRawPointerKind(pointerKind) || !(cPtr->isInt() || cPtr->isUInt() || cPtr->isInt8() || cPtr->isUInt8() || cPtr->isInt16() || cPtr->isUInt16() || cPtr->isInt32() || cPtr->isUInt32() || cPtr->isInt64() || cPtr->isUInt64())) { return SolutionKind::Error; } increaseScore(SK_ValueToPointerConversion, locator); type1 = getFixedTypeRecursive(type1->getInOutObjectType()->getArrayElementType(), /*wantRValue=*/false); LLVM_FALLTHROUGH; } case ConversionRestrictionKind::InoutToCPointer: { SmallVector optionals; auto ptr2 = type2->getDesugaredType()->lookThroughAllOptionalTypes(optionals); increaseScore(SK_ValueToOptional, locator, optionals.size()); PointerTypeKind pointerKind; (void)ptr2->getAnyPointerElementType(pointerKind); auto baseType1 = type1->getInOutObjectType(); Type ptr1; // The right-hand size is a raw pointer, so let's use `UnsafeMutablePointer` // for the `inout` type. if (pointerKind == PTK_UnsafeRawPointer || pointerKind == PTK_UnsafeMutableRawPointer) { ptr1 = BoundGenericType::get(Context.getUnsafeMutablePointerDecl(), /*parent=*/nullptr, {baseType1}); } else { ptr1 = baseType1->wrapInPointer(pointerKind); } assert(ptr1); return simplifyPointerToCPointerRestriction(ptr1, ptr2, flags, locator); } // T < U or T is bridged to V where V < U ===> Array case ConversionRestrictionKind::ArrayUpcast: { Type baseType1 = type1->getArrayElementType(); Type baseType2 = type2->getArrayElementType(); increaseScore(SK_CollectionUpcastConversion, locator); return matchTypes(baseType1, baseType2, matchKind, subflags, locator.withPathElement( LocatorPathElt::GenericArgument(0))); } // K1 < K2 && V1 < V2 || K1 bridges to K2 && V1 bridges to V2 ===> // Dictionary case ConversionRestrictionKind::DictionaryUpcast: { auto t1 = type1->getDesugaredType(); Type key1, value1; std::tie(key1, value1) = *isDictionaryType(t1); auto t2 = type2->getDesugaredType(); Type key2, value2; std::tie(key2, value2) = *isDictionaryType(t2); auto subMatchKind = matchKind; // TODO: Restrict this? increaseScore(SK_CollectionUpcastConversion, locator); // The source key and value types must be subtypes of the destination // key and value types, respectively. auto result = matchTypes(key1, key2, subMatchKind, subflags, locator.withPathElement(LocatorPathElt::GenericArgument(0))); if (result.isFailure()) return result; switch (matchTypes( value1, value2, subMatchKind, subflags, locator.withPathElement(LocatorPathElt::GenericArgument(1)))) { case SolutionKind::Solved: return result; case SolutionKind::Unsolved: return SolutionKind::Unsolved; case SolutionKind::Error: return SolutionKind::Error; } } // T1 < T2 || T1 bridges to T2 ===> Set case ConversionRestrictionKind::SetUpcast: { Type baseType1 = *isSetType(type1); Type baseType2 = *isSetType(type2); increaseScore(SK_CollectionUpcastConversion, locator); return matchTypes(baseType1, baseType2, matchKind, subflags, locator.withPathElement(LocatorPathElt::GenericArgument(0))); } // T1 T1 getRValueType()->lookThroughAllOptionalTypes()->isAnyHashable()) { return SolutionKind::Error; } addContextualScore(); increaseScore(SK_UserConversion, locator); // FIXME: Use separate score kind? if (worseThanBestSolution()) { return SolutionKind::Error; } auto hashableProtocol = getASTContext().getProtocol(KnownProtocolKind::Hashable); if (!hashableProtocol) return SolutionKind::Error; auto constraintLocator = getConstraintLocator(locator); auto tv = createTypeVariable(constraintLocator, TVO_PrefersSubtypeBinding | TVO_CanBindToNoEscape); addConstraint(ConstraintKind::ConformsTo, tv, hashableProtocol->getDeclaredInterfaceType(), constraintLocator); return matchTypes(type1, tv, ConstraintKind::Conversion, subflags, locator); } // T' < U and T a toll-free-bridged to T' ===> T' getClassOrBoundGenericClass(); auto bridgedObjCClass = nativeClass->getAttrs().getAttribute()->getObjCClass(); return matchTypes(bridgedObjCClass->getDeclaredInterfaceType(), type2, ConstraintKind::Subtype, subflags, locator); } // T < U' and U a toll-free-bridged to U' ===> T getClassOrBoundGenericClass(); auto bridgedObjCClass = nativeClass->getAttrs().getAttribute()->getObjCClass(); return matchTypes(type1, bridgedObjCClass->getDeclaredInterfaceType(), ConstraintKind::Subtype, subflags, locator); } case ConversionRestrictionKind::DoubleToCGFloat: case ConversionRestrictionKind::CGFloatToDouble: { // Prefer CGFloat -> Double over other way araund. auto impact = restriction == ConversionRestrictionKind::CGFloatToDouble ? 2 : 10; if (restriction == ConversionRestrictionKind::DoubleToCGFloat) { if (auto *anchor = locator.trySimplifyToExpr()) { if (auto depth = getExprDepth(anchor)) impact = (*depth + 1) * impact; } } else if (locator.directlyAt() || locator.endsWith()) { // Situations like: // // let _: Double = <> // = <> // // Used to be supported due to an incorrect fix added in // diagnostic mode. Lower impact here means that right-hand // side of the assignment is allowed to maintain CGFloat // until the very end which minimizes the number of conversions // used and keeps literals as Double when possible. impact = 1; } increaseScore(SK_ImplicitValueConversion, locator, impact); if (worseThanBestSolution()) return SolutionKind::Error; return SolutionKind::Solved; } } llvm_unreachable("bad conversion restriction"); } ConstraintSystem::SolutionKind ConstraintSystem::simplifyRestrictedConstraint( ConversionRestrictionKind restriction, Type type1, Type type2, ConstraintKind matchKind, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { switch (simplifyRestrictedConstraintImpl(restriction, type1, type2, matchKind, flags, locator)) { case SolutionKind::Solved: { // If we have an application of a non-ephemeral parameter, then record a // fix if we have to treat an ephemeral conversion as non-ephemeral. It's // important that this is solved as an independent constraint, as the // solving of this restriction may be required in order to evaluate it. For // example, when solving `foo(&.x)`, we need to first match types for the // inout-to-pointer conversion, which then allows us to resolve the overload // of `x`, which may or may not produce an ephemeral pointer. if (locator.isNonEphemeralParameterApplication()) { bool downgradeToWarning = !getASTContext().LangOpts.DiagnoseInvalidEphemeralnessAsError; auto *fix = TreatEphemeralAsNonEphemeral::create( *this, getConstraintLocator(locator), type1, type2, restriction, downgradeToWarning); addFixConstraint(fix, matchKind, type1, type2, locator); } addConversionRestriction(type1, type2, restriction); return SolutionKind::Solved; } case SolutionKind::Unsolved: return SolutionKind::Unsolved; case SolutionKind::Error: return SolutionKind::Error; } llvm_unreachable("Unhandled SolutionKind in switch."); } ConstraintSystem::SolutionKind ConstraintSystem::simplifyPointerToCPointerRestriction( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { bool inCorrectPosition = isArgumentOfImportedDecl(locator); if (!inCorrectPosition) { // If this is not an imported function, let's not proceed with // the conversion, unless in diagnostic mode. if (!shouldAttemptFixes()) return SolutionKind::Error; // Let's attempt to convert the types and record a tailored // fix if that succeeds. } auto &ctx = getASTContext(); PointerTypeKind swiftPtrKind, cPtrKind; auto swiftPtr = type1->getAnyPointerElementType(swiftPtrKind); auto cPtr = type2->getAnyPointerElementType(cPtrKind); assert(swiftPtr); assert(cPtr); auto markSupported = [&]() -> SolutionKind { // Make sure that solutions with implicit pointer conversions // are always worse than the ones without them. increaseScore(SK_ImplicitValueConversion, locator); if (inCorrectPosition) return SolutionKind::Solved; // If conversion cannot be allowed on account of declaration, // let's add a tailored fix. auto *fix = AllowSwiftToCPointerConversion::create( *this, getConstraintLocator(locator)); return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; }; auto elementLoc = locator.withPathElement(LocatorPathElt::GenericArgument(0)); if (swiftPtr->isTypeVariableOrMember()) { // Inference between the equivalent pointer kinds is // handled by regular pointer conversions. if (swiftPtrKind == cPtrKind) return SolutionKind::Error; addConstraint(ConstraintKind::BindToPointerType, swiftPtr, cPtr, elementLoc); return markSupported(); } // If pointers have the same element type there is nothing to do. if (swiftPtr->isEqual(cPtr)) return markSupported(); // Unsafe[Mutable]RawPointer -> Unsafe[Mutable]Pointer<[U]Int8> if (swiftPtrKind == PTK_UnsafeRawPointer || swiftPtrKind == PTK_UnsafeMutableRawPointer) { // Since it's a C pointer on parameter side it would always // be fully resolved. if (cPtr->isInt8() || cPtr->isUInt8()) return markSupported(); } else { // Unsafe[Mutable]Pointer -> Unsafe[Mutable]Pointer<[U]Int8> if (cPtr->isInt8() || cPtr->isUInt8()) { // can default to the type of C pointer. addConstraint(ConstraintKind::Defaultable, swiftPtr, cPtr, elementLoc); return markSupported(); } // Unsafe[Mutable]Pointer <-> // Unsafe[Mutable]Pointer if (swiftPtr->isInt() || swiftPtr->isUInt()) { addConstraint(ConstraintKind::Equal, cPtr, swiftPtr->isUInt() ? ctx.getIntType() : ctx.getUIntType(), elementLoc); return markSupported(); } if (swiftPtr->isInt8() || swiftPtr->isUInt8()) { addConstraint(ConstraintKind::Equal, cPtr, swiftPtr->isUInt8() ? ctx.getInt8Type() : ctx.getUInt8Type(), elementLoc); return markSupported(); } if (swiftPtr->isInt16() || swiftPtr->isUInt16()) { addConstraint(ConstraintKind::Equal, cPtr, swiftPtr->isUInt16() ? ctx.getInt16Type() : ctx.getUInt16Type(), elementLoc); return markSupported(); } if (swiftPtr->isInt32() || swiftPtr->isUInt32()) { addConstraint(ConstraintKind::Equal, cPtr, swiftPtr->isUInt32() ? ctx.getInt32Type() : ctx.getUInt32Type(), elementLoc); return markSupported(); } if (swiftPtr->isInt64() || swiftPtr->isUInt64()) { addConstraint(ConstraintKind::Equal, cPtr, swiftPtr->isUInt64() ? ctx.getInt64Type() : ctx.getUInt64Type(), elementLoc); return markSupported(); } } // If the conversion is unsupported, let's record a generic argument mismatch. if (shouldAttemptFixes() && !inCorrectPosition) { auto *fix = AllowArgumentMismatch::create(*this, type1, type2, getConstraintLocator(locator)); return recordFix(fix, /*impact=*/2) ? SolutionKind::Error : SolutionKind::Solved; } return SolutionKind::Error; } static bool isAugmentingFix(ConstraintFix *fix) { switch (fix->getKind()) { case FixKind::TreatRValueAsLValue: return false; default: return true; } } bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) { if (isDebugMode()) { auto &log = llvm::errs(); log.indent(solverState ? solverState->getCurrentIndent() : 0) << "(attempting fix "; fix->print(log); log << ")\n"; } if (hasArgumentsIgnoredForCodeCompletion()) { // Avoid simplifying the locator if the constraint system didn't ignore any // arguments. auto argExpr = simplifyLocatorToAnchor(fix->getLocator()); if (isArgumentIgnoredForCodeCompletion(getAsExpr(argExpr))) { // The argument was ignored. Don't record any fixes for it. return false; } } // Record the fix. // If this should affect the solution score, do so. if (auto impactScoreKind = fix->impact()) increaseScore(*impactScoreKind, fix->getLocator(), impact); // If we've made the current solution worse than the best solution we've seen // already, stop now. if (worseThanBestSolution()) return true; if (isAugmentingFix(fix)) { addFix(fix); return false; } auto anchor = fix->getAnchor(); assert(bool(anchor) && "non-augmenting fix without an anchor?"); // Only useful to record if no pre-existing fix is associated with // current anchor or, in case of anchor being an expression, any of // its sub-expressions. llvm::SmallDenseSet anchors; for (const auto *fix : Fixes) { // Fixes that don't affect the score shouldn't be considered because even // if such a fix is recorded at that anchor this should not // have any affect in the recording of any other fix. if (!fix->impact()) continue; anchors.insert(fix->getAnchor()); } bool found = false; if (auto *expr = getAsExpr(anchor)) { forEachExpr(expr, [&](Expr *subExpr) -> Expr * { found |= anchors.count(subExpr); return subExpr; }); } else { found = anchors.count(anchor); } if (!found) addFix(fix); return false; } void ConstraintSystem::recordPotentialHole(TypeVariableType *typeVar) { typeVar->getImpl().enableCanBindToHole(getTrail()); } void ConstraintSystem::recordAnyTypeVarAsPotentialHole(Type type) { if (!type->hasTypeVariable()) return; type.visit([&](Type type) { if (auto *typeVar = type->getAs()) { typeVar->getImpl().enableCanBindToHole(getTrail()); } }); } void ConstraintSystem::recordTypeVariablesAsHoles(Type type) { type.visit([&](Type componentTy) { if (auto *typeVar = componentTy->getAs()) { // Ignore bound type variables. This can happen if a type variable // occurs in multiple positions and/or if type hasn't been fully // simplified before this call. if (typeVar->getImpl().hasRepresentativeOrFixed()) return; assignFixedType(typeVar, PlaceholderType::get(getASTContext(), typeVar)); } }); } void ConstraintSystem::recordMatchCallArgumentResult( ConstraintLocator *locator, MatchCallArgumentResult result) { assert(locator->isLastElement()); bool inserted = argumentMatchingChoices.insert({locator, result}).second; ASSERT(inserted); if (solverState) recordChange(SolverTrail::Change::RecordedMatchCallArgumentResult(locator)); } void ConstraintSystem::recordImplicitCallAsFunctionRoot( ConstraintLocator *locator, UnresolvedDotExpr *root) { bool inserted = ImplicitCallAsFunctionRoots.insert({locator, root}).second; ASSERT(inserted); if (solverState) recordChange(SolverTrail::Change::RecordedImplicitCallAsFunctionRoot(locator)); } void ConstraintSystem::recordKeyPath(const KeyPathExpr *keypath, TypeVariableType *root, TypeVariableType *value, DeclContext *dc) { bool inserted = KeyPaths.insert( std::make_pair(keypath, std::make_tuple(root, value, dc))).second; ASSERT(inserted); if (solverState) { recordChange(SolverTrail::Change::RecordedKeyPath( const_cast(keypath))); } } void ConstraintSystem::removeKeyPath(const KeyPathExpr *keypath) { bool erased = KeyPaths.erase(keypath); ASSERT(erased); } ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( ConstraintFix *fix, Type type1, Type type2, ConstraintKind matchKind, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { // Try with the fix. TypeMatchOptions subflags = getDefaultDecompositionOptions(flags) | TMF_ApplyingFix; switch (fix->getKind()) { case FixKind::ForceOptional: { SmallVector unwraps1; type1->lookThroughAllOptionalTypes(unwraps1); SmallVector unwraps2; type2->lookThroughAllOptionalTypes(unwraps2); unsigned impact = 1; if (unwraps1.size() > unwraps2.size()) impact = unwraps1.size() - unwraps2.size(); else if (unwraps2.size() > unwraps1.size()) impact = unwraps2.size() - unwraps1.size(); return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::UnwrapOptionalBase: case FixKind::UnwrapOptionalBaseWithOptionalResult: { if (recordFix(fix)) return SolutionKind::Error; type1 = simplifyType(type1); type2 = simplifyType(type2); // Explicitly preserve l-valueness of an unwrapped member type. if (!type1->is() && type2->is()) type1 = LValueType::get(type1); // First type already appropriately set. return matchTypes(type1, type2, matchKind, subflags, locator); } case FixKind::ForceDowncast: // These work whenever they are suggested. if (recordFix(fix)) return SolutionKind::Error; return SolutionKind::Solved; case FixKind::AddressOf: { // Assume that '&' was applied to the first type, turning an lvalue into // an inout. auto result = matchTypes(InOutType::get(type1->getRValueType()), type2, matchKind, subflags, locator); if (result == SolutionKind::Solved) if (recordFix(fix)) return SolutionKind::Error; return result; } case FixKind::AllowTupleTypeMismatch: { if (fix->getAs()->isElementMismatch()) { auto *locator = fix->getLocator(); if (recordFix(fix, /*impact*/locator->isForContextualType() ? 5 : 1)) return SolutionKind::Error; return SolutionKind::Solved; } auto lhs = type1->castTo(); auto rhs = type2->castTo(); // Create a new tuple type the size of the smaller tuple with elements // from the larger tuple whenever either side contains a type variable. // For example (A, $0, B, $2) and (X, Y, $1) produces: (X, $0, B). // This allows us to guarantee that the types will match, and all // type variables will get bound to something as long as we default // excess types in the larger tuple to Any. In the prior example, // when the tuples (X, Y, $1) and (X, $0, B) get matched, $0 is equated // to Y, $1 is equated to B, and $2 is defaulted to Any. auto lhsLarger = lhs->getNumElements() >= rhs->getNumElements(); auto isLabelingFailure = lhs->getNumElements() == rhs->getNumElements(); auto larger = lhsLarger ? lhs : rhs; auto smaller = lhsLarger ? rhs : lhs; llvm::SmallVector newTupleTypes; // FIXME: For now, if either side contains pack expansion types, consider // the fix constraint solved without trying to figure out which tuple // elements were part of the pack. { if (containsPackExpansionType(lhs) || containsPackExpansionType(rhs)) { if (recordFix(fix)) return SolutionKind::Error; return SolutionKind::Solved; } } for (unsigned i = 0; i < larger->getNumElements(); ++i) { auto largerElt = larger->getElement(i); if (i < smaller->getNumElements()) { auto smallerElt = smaller->getElement(i); if (isLabelingFailure) newTupleTypes.push_back(TupleTypeElt(largerElt.getType())); else if (largerElt.getType()->isTypeVariableOrMember() || smallerElt.getType()->isTypeVariableOrMember()) newTupleTypes.push_back(largerElt); else newTupleTypes.push_back(smallerElt); } else { if (largerElt.getType()->isTypeVariableOrMember()) recordAnyTypeVarAsPotentialHole(largerElt.getType()); } } auto matchingType = TupleType::get(newTupleTypes, getASTContext()); if (recordFix(fix)) return SolutionKind::Error; return matchTupleTypes(matchingType, smaller, matchKind, subflags, locator); } case FixKind::AllowFunctionTypeMismatch: { if (recordFix(fix, /*impact=*/5)) return SolutionKind::Error; return SolutionKind::Solved; } case FixKind::TreatEphemeralAsNonEphemeral: { auto *theFix = static_cast(fix); // If we have a non-ephemeral locator for an ephemeral conversion, make a // note of the fix. auto conversion = theFix->getConversionKind(); switch (isConversionEphemeral(conversion, locator)) { case ConversionEphemeralness::Ephemeral: // Record the fix with an impact of zero. This ensures that non-ephemeral // diagnostics don't impact solver behavior. if (recordFix(fix, /*impact*/ 0)) return SolutionKind::Error; return SolutionKind::Solved; case ConversionEphemeralness::Unresolved: case ConversionEphemeralness::NonEphemeral: // FIXME: The unresolved case should form an unsolved constraint rather // than being treated as fully solved. This will require a way to connect // the unsolved constraint to the type variable for the unresolved // overload such that the fix gets re-activated when the overload is // bound. return SolutionKind::Solved; } } case FixKind::AllowSendingMismatch: case FixKind::InsertCall: case FixKind::RemoveReturn: case FixKind::RemoveAddressOf: case FixKind::AddMissingArguments: case FixKind::MoveOutOfOrderArgument: case FixKind::SkipUnhandledConstructInResultBuilder: case FixKind::UsePropertyWrapper: case FixKind::UseWrappedValue: case FixKind::AllowInvalidPropertyWrapperType: case FixKind::RemoveProjectedValueArgument: case FixKind::ExpandArrayIntoVarargs: case FixKind::UseRawValue: case FixKind::SpecifyBaseTypeForContextualMember: case FixKind::CoerceToCheckedCast: case FixKind::SpecifyObjectLiteralTypeImport: case FixKind::AllowKeyPathRootTypeMismatch: case FixKind::UnwrapOptionalBaseKeyPathApplication: case FixKind::AllowCoercionToForceCast: case FixKind::SpecifyKeyPathRootType: case FixKind::SpecifyLabelToAssociateTrailingClosure: case FixKind::AllowKeyPathWithoutComponents: case FixKind::IgnoreInvalidResultBuilderBody: case FixKind::IgnoreResultBuilderWithReturnStmts: case FixKind::SpecifyContextualTypeForNil: case FixKind::AllowRefToInvalidDecl: case FixKind::SpecifyBaseTypeForOptionalUnresolvedMember: case FixKind::SpecifyPackElementType: case FixKind::AllowCheckedCastCoercibleOptionalType: case FixKind::AllowNoopCheckedCast: case FixKind::AllowNoopExistentialToCFTypeCheckedCast: case FixKind::AllowUnsupportedRuntimeCheckedCast: case FixKind::AllowCheckedCastToUnrelated: case FixKind::AllowInvalidStaticMemberRefOnProtocolMetatype: case FixKind::AllowWrappedValueMismatch: case FixKind::RemoveExtraneousArguments: case FixKind::SpecifyTypeForPlaceholder: case FixKind::AllowAutoClosurePointerConversion: case FixKind::NotCompileTimeLiteral: case FixKind::RenameConflictingPatternVariables: case FixKind::AllowInvalidPackElement: case FixKind::AllowInvalidPackReference: case FixKind::AllowInvalidPackExpansion: case FixKind::IgnoreWhereClauseInPackIteration: case FixKind::MacroMissingPound: case FixKind::AllowGlobalActorMismatch: case FixKind::AllowAssociatedValueMismatch: case FixKind::AllowConcreteTypeSpecialization: case FixKind::AllowFunctionSpecialization: case FixKind::IgnoreGenericSpecializationArityMismatch: case FixKind::IgnoreKeyPathSubscriptIndexMismatch: case FixKind::AllowMemberRefOnExistential: case FixKind::AllowNonClassTypeToConvertToAnyObject: { return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::GenericArgumentsMismatch: { unsigned impact = 1; if (type1->isMarkerExistential() || type2->isMarkerExistential()) ++impact; // If generic arguments mismatch ends up being recorded on the result // of the chain or a try expression it means that there is a contextual // conversion mismatch. // // For optional conversions the solver currently generates a disjunction // with two choices - bind and optional-to-optional conversion which is // anchored on the contextual expression. If we can get a fix recorded // there that would result in a better diagnostic. It's only possible // for optional-to-optional choice because it doesn't bind the // variable immediately, so we need to downgrade direct fixes to prevent // `bind` choice from considered better. if (fix->directlyAt() || fix->directlyAt()) impact += 2; return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::IgnoreThrownErrorMismatch: { return recordFix(fix, 2) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::IgnoreInvalidASTNode: { return recordFix(fix, 10) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::IgnoreUnresolvedPatternVar: { return recordFix(fix, 100) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::AllowInvalidMemberReferenceInInitAccessor: { return recordFix(fix, 5) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::ExplicitlyConstructRawRepresentable: { // Let's increase impact of this fix for binary operators because // it's possible to get both `.rawValue` and construction fixes for // different overloads of a binary operator and `.rawValue` is a // better fix because raw representable has a failable constructor. return recordFix(fix, /*impact=*/isExpr(locator.getAnchor()) ? 2 : 1) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::TreatRValueAsLValue: { unsigned impact = TreatRValueAsLValue::assessImpact(*this, fix->getLocator()); return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::AddConformance: case FixKind::SkipSameTypeRequirement: case FixKind::SkipSameShapeRequirement: case FixKind::SkipSuperclassRequirement: { return recordFix(fix, assessRequirementFailureImpact(*this, type1, fix->getLocator())) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::AllowArgumentTypeMismatch: case FixKind::IgnoreDefaultExprTypeMismatch: { auto impact = 2; // If there are any other argument mismatches already detected for this // call, we increase the score even higher so more argument fixes means // less viable is the overload. if (llvm::any_of(getFixes(), [&](const ConstraintFix *fix) { auto *fixLocator = fix->getLocator(); return fixLocator->findLast() ? fixLocator->getAnchor() == locator.getAnchor() : false; })) impact += 3; // Passing a closure to a parameter that doesn't expect one should // be scored lower because there might be an overload that expects // a closure but has other issues e.g. wrong number of parameters. if (!type2->lookThroughAllOptionalTypes()->is()) { auto argument = simplifyLocatorToAnchor(fix->getLocator()); if (isExpr(argument)) { impact += 2; } } // De-prioritize `Builtin.RawPointer` and `OpaquePointer` parameters // because they usually clash with generic parameter mismatches e.g. // // let ptr: UnsafePointer = ... // _ = UnsafePointer(ups) // // Here initializer overloads have both `Builtin.RawPointer` and // `OpaquePointer` variants, but the actual issue is that generic argument // `String` doesn't match `Int`. { if (type2->is()) impact += 1; if (type2->getAnyNominal() == getASTContext().getOpaquePointerDecl()) impact += 1; } return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::TreatArrayLiteralAsDictionary: { ArrayExpr *AE = getAsExpr(fix->getAnchor()); assert(AE); // If the array was empty, there's nothing to do. if (AE->getNumElements() == 0) return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; // For arrays with a single element, match the element type to the // dictionary's key type. SmallVector optionals; auto dictTy = type2->lookThroughAllOptionalTypes(optionals); // If the fix is worse than the best solution, there's no point continuing. if (recordFix(fix, optionals.size() + 1)) return SolutionKind::Error; // Extract the dictionary key type. ProtocolDecl *dictionaryProto = Context.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral); auto keyAssocTy = dictionaryProto->getAssociatedType(Context.Id_Key); auto valueBaseTy = createTypeVariable(getConstraintLocator(locator), TVO_CanBindToLValue | TVO_CanBindToNoEscape | TVO_CanBindToHole); assignFixedType(valueBaseTy, dictTy); auto dictionaryKeyTy = DependentMemberType::get(valueBaseTy, keyAssocTy); // Extract the array element type. auto elemTy = type1->getArrayElementType(); ConstraintLocator *elemLoc = getConstraintLocator(AE->getElement(0)); ConstraintKind kind = isDictionaryType(dictTy) ? ConstraintKind::Conversion : ConstraintKind::Equal; return matchTypes(elemTy, dictionaryKeyTy, kind, subflags, elemLoc); } case FixKind::ContextualMismatch: case FixKind::IgnoreContextualType: case FixKind::IgnoreAssignmentDestinationType: case FixKind::AllowConversionThroughInOut: case FixKind::IgnoreCollectionElementContextualMismatch: { auto impact = 1; auto locator = fix->getLocator(); if (auto branchElt = locator->getLastElementAs()) { // If this is `else` branch of a ternary operator, let's // increase its impact to eliminate the chance of ambiguity. // // Branches are connected through two `subtype` constraints // to a common type variable with represents their join, which // means that result would attempt a type from each side if // one is available and that would result in two fixes - one for // each mismatched branch. if (branchElt->forElse()) { impact = 10; } else { // Also increase impact for `then` branch lower than `else` to still // eliminate ambiguity, but slightly worst than the average fix to avoid // so the solution which record this fix wouldn't be picked over one // that has contextual mismatch fix on the result of ternary expression. impact = 5; } } using SingleValueStmtResult = LocatorPathElt::SingleValueStmtResult; if (auto branchElt = locator->getLastElementAs()) { // Similar to a ternary, except we have N branches. Let's prefer the fix // on the first branch, and discount subsequent branches by index. if (branchElt->getIndex() > 0) impact = 9 + branchElt->getIndex(); } // Increase impact of invalid conversions to `Any` and `AnyHashable` // associated with collection elements (i.e. for-in sequence element) // because it means that other side is structurally incompatible. if (fix->getKind() == FixKind::IgnoreCollectionElementContextualMismatch) { if (type2->isAny() || type2->isAnyHashable()) ++impact; } if (recordFix(fix, impact)) return SolutionKind::Error; if (auto *fnType1 = type1->getAs()) { // If this is a contextual mismatch between two // function types which we couldn't find a more // specific fix for. Let's assume that such types // are completely disjoint and adjust impact of // the fix accordingly. if (type2->is()) { increaseScore(SK_Fix, locator, 10); } else { // If type produced by expression is a function type // with result type matching contextual, it should have // been diagnosed as "missing explicit call", let's // increase the score to make sure that we don't impede that. auto result = matchTypes(fnType1->getResult(), type2, matchKind, TMF_ApplyingFix, locator); if (result == SolutionKind::Solved) increaseScore(SK_Fix, locator); } } return SolutionKind::Solved; } case FixKind::AllowNonOptionalWeak: { if (recordFix(fix)) return SolutionKind::Error; // NOTE: The order here is important! Pattern matching equality is // not symmetric (we need to fix that either by using a different // constraint, or actually making it symmetric). (void)matchTypes(OptionalType::get(type1), type2, ConstraintKind::Equal, TypeMatchFlags::TMF_ApplyingFix, locator); return SolutionKind::Solved; } case FixKind::UseSubscriptOperator: case FixKind::ExplicitlyEscaping: case FixKind::MarkGlobalActorFunction: case FixKind::RelabelArguments: case FixKind::RemoveCall: case FixKind::RemoveUnwrap: case FixKind::DefineMemberBasedOnUse: case FixKind::AllowTypeOrInstanceMember: case FixKind::AllowInvalidPartialApplication: case FixKind::AllowInvalidInitRef: case FixKind::AllowClosureParameterDestructuring: case FixKind::AllowInaccessibleMember: case FixKind::AllowAnyObjectKeyPathRoot: case FixKind::AllowMultiArgFuncKeyPathMismatch: case FixKind::TreatKeyPathSubscriptIndexAsHashable: case FixKind::AllowInvalidRefInKeyPath: case FixKind::DefaultGenericArgument: case FixKind::AllowMutatingMemberOnRValueBase: case FixKind::AllowTupleSplatForSingleParameter: case FixKind::SpecifyClosureParameterType: case FixKind::SpecifyClosureReturnType: case FixKind::AddQualifierToAccessTopLevelName: case FixKind::AddSendableAttribute: case FixKind::DropThrowsAttribute: case FixKind::DropAsyncAttribute: case FixKind::AllowSwiftToCPointerConversion: case FixKind::AllowTupleLabelMismatch: case FixKind::AddExplicitExistentialCoercion: case FixKind::DestructureTupleToMatchPackExpansionParameter: case FixKind::AllowValueExpansionWithoutPackReferences: case FixKind::IgnoreInvalidPatternInExpr: case FixKind::IgnoreInvalidPlaceholder: case FixKind::IgnoreOutOfPlaceThenStmt: case FixKind::IgnoreMissingEachKeyword: case FixKind::AllowInlineArrayLiteralCountMismatch: case FixKind::IgnoreIsolatedConformance: llvm_unreachable("handled elsewhere"); } llvm_unreachable("Unhandled FixKind in switch."); } ConstraintSystem::SolutionKind ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first, Type second, ConstraintLocatorBuilder locator, bool isFavored) { assert(first && "Missing first type"); assert(second && "Missing second type"); TypeMatchOptions subflags = TMF_GenerateConstraints; switch (kind) { case ConstraintKind::Equal: case ConstraintKind::Bind: case ConstraintKind::BindParam: case ConstraintKind::BindToPointerType: case ConstraintKind::Subtype: case ConstraintKind::Conversion: return matchTypes(first, second, kind, subflags, locator); case ConstraintKind::ArgumentConversion: case ConstraintKind::OperatorArgumentConversion: return addArgumentConversionConstraintImpl(kind, first, second, locator); case ConstraintKind::BridgingConversion: return simplifyBridgingConstraint(first, second, subflags, locator); case ConstraintKind::DynamicCallableApplicableFunction: return simplifyDynamicCallableApplicableFnConstraint(first, second, subflags, locator); case ConstraintKind::DynamicTypeOf: return simplifyDynamicTypeOfConstraint(first, second, subflags, locator); case ConstraintKind::EscapableFunctionOf: return simplifyEscapableFunctionOfConstraint(first, second, subflags, locator); case ConstraintKind::OpenedExistentialOf: return simplifyOpenedExistentialOfConstraint(first, second, subflags, locator); case ConstraintKind::SubclassOf: return simplifySubclassOfConstraint(first, second, locator, subflags); case ConstraintKind::NonisolatedConformsTo: case ConstraintKind::ConformsTo: case ConstraintKind::LiteralConformsTo: return simplifyConformsToConstraint(first, second, kind, locator, subflags); case ConstraintKind::TransitivelyConformsTo: return simplifyTransitivelyConformsTo(first, second, locator, subflags); case ConstraintKind::CheckedCast: return simplifyCheckedCastConstraint(first, second, subflags, locator); case ConstraintKind::OptionalObject: return simplifyOptionalObjectConstraint(first, second, subflags, locator); case ConstraintKind::Defaultable: return simplifyDefaultableConstraint(first, second, subflags, locator); case ConstraintKind::PropertyWrapper: return simplifyPropertyWrapperConstraint(first, second, subflags, locator); case ConstraintKind::OneWayEqual: return simplifyOneWayConstraint(kind, first, second, subflags, locator); case ConstraintKind::UnresolvedMemberChainBase: return simplifyUnresolvedMemberChainBaseConstraint(first, second, subflags, locator); case ConstraintKind::BindTupleOfFunctionParams: return simplifyBindTupleOfFunctionParamsConstraint(first, second, subflags, locator); case ConstraintKind::PackElementOf: return simplifyPackElementOfConstraint(first, second, subflags, locator); case ConstraintKind::ShapeOf: return simplifyShapeOfConstraint(first, second, subflags, locator); case ConstraintKind::SameShape: return simplifySameShapeConstraint(first, second, subflags, locator); case ConstraintKind::ExplicitGenericArguments: return simplifyExplicitGenericArgumentsConstraint( first, second, subflags, locator); case ConstraintKind::MaterializePackExpansion: return simplifyMaterializePackExpansionConstraint(first, second, subflags, locator); case ConstraintKind::LValueObject: return simplifyLValueObjectConstraint(first, second, subflags, locator); case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueWitness: case ConstraintKind::BindOverload: case ConstraintKind::Disjunction: case ConstraintKind::Conjunction: case ConstraintKind::KeyPath: case ConstraintKind::KeyPathApplication: case ConstraintKind::FallbackType: case ConstraintKind::SyntacticElement: case ConstraintKind::ApplicableFunction: llvm_unreachable("Use the correct addConstraint()"); } llvm_unreachable("Unhandled ConstraintKind in switch."); } ConstraintSystem::SolutionKind ConstraintSystem::addArgumentConversionConstraintImpl( ConstraintKind kind, Type first, Type second, ConstraintLocatorBuilder locator) { assert(kind == ConstraintKind::ArgumentConversion || kind == ConstraintKind::OperatorArgumentConversion); // If we have an unresolved closure argument, form an unsolved argument // conversion constraint, making sure to reference the type variables for // a result builder if applicable. This ensures we properly connect the // closure type variable with any type variables in the result builder, as // such type variables will be accessible within the body of the closure when // we open it. first = getFixedTypeRecursive(first, /*rvalue*/ false); if (auto *argTypeVar = first->getAs()) { if (argTypeVar->getImpl().isClosureType()) { // Extract any type variables present in the parameter's result builder. SmallPtrSet typeVars; if (auto builderTy = getOpenedResultBuilderTypeFor(*this, locator)) builderTy->getTypeVariables(typeVars); SmallVector referencedVars{typeVars.begin(), typeVars.end()}; auto *loc = getConstraintLocator(locator); addUnsolvedConstraint( Constraint::create(*this, kind, first, second, loc, referencedVars)); return SolutionKind::Solved; } } return matchTypes(first, second, kind, TMF_GenerateConstraints, locator); } void ConstraintSystem::addKeyPathApplicationRootConstraint(Type root, ConstraintLocatorBuilder locator) { // If this is a subscript with a KeyPath expression, add a constraint that // connects the subscript's root type to the root type of the KeyPath. SmallVector path; auto anchor = locator.getLocatorParts(path); auto subscript = getAsExpr(anchor); if (!subscript) return; assert((path.size() == 1 && path[0].getKind() == ConstraintLocator::SubscriptMember) || (path.size() == 2 && path[1].getKind() == ConstraintLocator::KeyPathDynamicMember)); // If a keypath subscript is used without the expected `keyPath:` label, // continue with type-checking when attempting fixes so that it gets caught // by the argument label checking. auto *argList = subscript->getArgs(); auto *unaryArg = argList->getUnaryExpr(); assert(unaryArg && "Expected KeyPathExpr apply to have single argument"); auto *keyPathExpr = dyn_cast(unaryArg); if (!keyPathExpr) return; auto typeVar = getType(keyPathExpr)->getAs(); if (!typeVar) return; auto constraints = CG.gatherNearbyConstraints( typeVar, [&keyPathExpr](Constraint *constraint) -> bool { if (constraint->getKind() != ConstraintKind::KeyPath) return false; auto *locator = constraint->getLocator(); if (auto KPE = getAsExpr(locator->getAnchor())) return KPE == keyPathExpr; return false; }); for (auto constraint : constraints) { auto keyPathRootTy = constraint->getSecondType(); addConstraint(ConstraintKind::Subtype, root->getWithoutSpecifierType(), keyPathRootTy, locator); } } void ConstraintSystem::addKeyPathApplicationConstraint(Type keypath, Type root, Type value, ConstraintLocatorBuilder locator, bool isFavored) { addKeyPathApplicationRootConstraint(root, locator); switch (simplifyKeyPathApplicationConstraint(keypath, root, value, TMF_GenerateConstraints, locator)) { case SolutionKind::Error: if (shouldRecordFailedConstraint()) { auto c = Constraint::create(*this, ConstraintKind::KeyPathApplication, keypath, root, value, getConstraintLocator(locator)); if (isFavored) c->setFavored(); recordFailedConstraint(c); } return; case SolutionKind::Solved: return; case SolutionKind::Unsolved: llvm_unreachable("should have generated constraints"); } } void ConstraintSystem::addKeyPathConstraint( Type keypath, Type root, Type value, ArrayRef componentTypeVars, ConstraintLocatorBuilder locator, bool isFavored) { switch (simplifyKeyPathConstraint(keypath, root, value, componentTypeVars, TMF_GenerateConstraints, locator)) { case SolutionKind::Error: if (shouldRecordFailedConstraint()) { auto c = Constraint::create(*this, ConstraintKind::KeyPath, keypath, root, value, getConstraintLocator(locator), componentTypeVars); if (isFavored) c->setFavored(); recordFailedConstraint(c); } return; case SolutionKind::Solved: return; case SolutionKind::Unsolved: llvm_unreachable("should have generated constraints"); } } void ConstraintSystem::addConstraint(Requirement req, ConstraintLocatorBuilder locator, bool isFavored, bool prohibitNonisolatedConformance) { bool conformsToAnyObject = false; std::optional kind; switch (req.getKind()) { case RequirementKind::SameShape: { auto type1 = req.getFirstType(); auto type2 = req.getSecondType(); addConstraint(ConstraintKind::SameShape, type1, type2, locator); return; } case RequirementKind::Conformance: kind = prohibitNonisolatedConformance ? ConstraintKind::NonisolatedConformsTo : ConstraintKind::ConformsTo; break; case RequirementKind::Superclass: { // FIXME: Should always use ConstraintKind::SubclassOf, but that breaks // a couple of diagnostics if (auto *typeVar = req.getFirstType()->getAs()) { if (typeVar->getImpl().canBindToPack()) { kind = ConstraintKind::SubclassOf; break; } } conformsToAnyObject = true; kind = ConstraintKind::Subtype; break; } case RequirementKind::SameType: kind = ConstraintKind::Bind; break; case RequirementKind::Layout: // Only a class constraint can be modeled as a constraint, and only that can // appear outside of a @_specialize at the moment anyway. if (req.getLayoutConstraint()->isClass()) { conformsToAnyObject = true; break; } else { llvm_unreachable("unexpected LayoutConstraint kind"); } return; } auto firstType = req.getFirstType(); if (kind) { addConstraint(*kind, req.getFirstType(), req.getSecondType(), locator, isFavored); } if (conformsToAnyObject) { auto anyObject = getASTContext().getAnyObjectConstraint(); addConstraint(ConstraintKind::ConformsTo, firstType, anyObject, locator); } } void ConstraintSystem::addConstraint(ConstraintKind kind, Type first, Type second, ConstraintLocatorBuilder locator, bool isFavored) { switch (addConstraintImpl(kind, first, second, locator, isFavored)) { case SolutionKind::Error: // Add a failing constraint, if needed. if (shouldRecordFailedConstraint()) { auto c = Constraint::create(*this, kind, first, second, getConstraintLocator(locator)); if (isFavored) c->setFavored(); recordFailedConstraint(c); } return; case SolutionKind::Unsolved: llvm_unreachable("should have generated constraints"); case SolutionKind::Solved: return; } } void ConstraintSystem::addApplicationConstraint( FunctionType *appliedFn, Type calleeType, std::optional trailingClosureMatching, DeclContext *useDC, ConstraintLocatorBuilder locator) { auto recordFailure = [&]() { if (shouldRecordFailedConstraint()) { auto *c = Constraint::createApplicableFunction( *this, appliedFn, calleeType, trailingClosureMatching, useDC, getConstraintLocator(locator)); recordFailedConstraint(c); } }; // First try to simplify the overload set for the function being applied. if (simplifyAppliedOverloads(calleeType, appliedFn, locator)) { recordFailure(); return; } switch (simplifyApplicableFnConstraint(appliedFn, calleeType, trailingClosureMatching, useDC, TMF_GenerateConstraints, locator)) { case SolutionKind::Error: recordFailure(); break; case SolutionKind::Unsolved: llvm_unreachable("should have generated constraints"); case SolutionKind::Solved: return; } } void ConstraintSystem::addContextualConversionConstraint( Expr *expr, Type conversionType, ContextualTypePurpose purpose, ConstraintLocator *locator) { if (conversionType.isNull()) return; // Determine the type of the constraint. auto constraintKind = ConstraintKind::Conversion; switch (purpose) { case CTP_ReturnStmt: case CTP_Initialization: { if (conversionType->is()) constraintKind = ConstraintKind::Equal; // Alternatively, we might have a nested opaque archetype, e.g. `(some P)?`. // In that case, we want `ConstraintKind::Conversion`. break; } case CTP_CallArgument: constraintKind = ConstraintKind::ArgumentConversion; break; case CTP_YieldByReference: // In a by-reference yield, we expect the contextual type to be an // l-value type, so the result must be bound to that. constraintKind = ConstraintKind::Bind; break; case CTP_DiscardStmt: // For the 'discard X', we always expect the contextual type to be // equal to the type of 'self'. constraintKind = ConstraintKind::Equal; break; case CTP_ForEachSequence: // Sequence expression associated with `for-in` loop has to conform // to `Sequence` or `AsyncSequence` protocol depending on the context. constraintKind = ConstraintKind::ConformsTo; break; case CTP_ArrayElement: case CTP_AssignSource: case CTP_CalleeResult: case CTP_CannotFail: case CTP_Condition: case CTP_Unused: case CTP_YieldByValue: case CTP_CaseStmt: case CTP_ThrowStmt: case CTP_EnumCaseRawValue: case CTP_DefaultParameter: case CTP_AutoclosureDefaultParameter: case CTP_ClosureResult: case CTP_DictionaryKey: case CTP_DictionaryValue: case CTP_CoerceOperand: case CTP_SubscriptAssignSource: case CTP_ForEachStmt: case CTP_WrappedProperty: case CTP_ComposedPropertyWrapper: case CTP_ExprPattern: case CTP_SingleValueStmtBranch: break; } // Add the constraint. // FIXME: This is the wrong place to be opening the opaque type. auto openedType = openOpaqueType(conversionType, purpose, locator, /*ownerDecl=*/nullptr); addConstraint(constraintKind, getType(expr), openedType, locator, /*isFavored*/ true); } void ConstraintSystem::addFixConstraint(ConstraintFix *fix, ConstraintKind kind, Type first, Type second, ConstraintLocatorBuilder locator, bool isFavored) { TypeMatchOptions subflags = TMF_GenerateConstraints; switch (simplifyFixConstraint(fix, first, second, kind, subflags, locator)) { case SolutionKind::Error: // Add a failing constraint, if needed. if (shouldRecordFailedConstraint()) { auto c = Constraint::createFixed(*this, kind, fix, first, second, getConstraintLocator(locator)); if (isFavored) c->setFavored(); recordFailedConstraint(c); } return; case SolutionKind::Unsolved: llvm_unreachable("should have generated constraints"); case SolutionKind::Solved: return; } } void ConstraintSystem::addExplicitConversionConstraint( Type fromType, Type toType, RememberChoice_t rememberChoice, ConstraintLocatorBuilder locator, ConstraintFix *compatFix) { SmallVector constraints; auto locatorPtr = getConstraintLocator(locator); // Coercion (the common case). Constraint *coerceConstraint = Constraint::create(*this, ConstraintKind::Conversion, fromType, toType, locatorPtr); coerceConstraint->setFavored(); constraints.push_back(coerceConstraint); // The source type can be explicitly converted to the destination type. Constraint *bridgingConstraint = Constraint::create(*this, ConstraintKind::BridgingConversion, fromType, toType, locatorPtr); constraints.push_back(bridgingConstraint); // If we're allowed to use a compatibility fix that emits a warning on // failure, add it to the disjunction so that it's recorded on failure. if (compatFix) { constraints.push_back( Constraint::createFixed(*this, ConstraintKind::BridgingConversion, compatFix, fromType, toType, locatorPtr)); } addDisjunctionConstraint(constraints, locator, rememberChoice); } TypeVariableType *ConstraintSystem::addMaterializePackExpansionConstraint( Type tupleType, ConstraintLocatorBuilder locator) { assert(isSingleUnlabeledPackExpansionTuple(tupleType)); TypeVariableType *packVar = createTypeVariable(getConstraintLocator(locator), TVO_CanBindToPack); addConstraint(ConstraintKind::MaterializePackExpansion, tupleType, packVar, getConstraintLocator(locator, {ConstraintLocator::Member})); return packVar; } ConstraintSystem::SolutionKind ConstraintSystem::simplifyConstraint(const Constraint &constraint) { auto matchKind = constraint.getKind(); switch (matchKind) { case ConstraintKind::Bind: case ConstraintKind::Equal: case ConstraintKind::BindParam: case ConstraintKind::BindToPointerType: case ConstraintKind::Subtype: case ConstraintKind::Conversion: case ConstraintKind::ArgumentConversion: case ConstraintKind::OperatorArgumentConversion: { // Relational constraints. // If there is a fix associated with this constraint, apply it. if (auto fix = constraint.getFix()) { return simplifyFixConstraint(fix, constraint.getFirstType(), constraint.getSecondType(), matchKind, std::nullopt, constraint.getLocator()); } // If there is a restriction on this constraint, apply it directly rather // than going through the general \c matchTypes() machinery. if (auto restriction = constraint.getRestriction()) { return simplifyRestrictedConstraint( *restriction, constraint.getFirstType(), constraint.getSecondType(), matchKind, std::nullopt, constraint.getLocator()); } return matchTypes(constraint.getFirstType(), constraint.getSecondType(), matchKind, std::nullopt, constraint.getLocator()); } case ConstraintKind::BridgingConversion: // If there is a fix associated with this constraint, apply it. if (auto fix = constraint.getFix()) { return simplifyFixConstraint(fix, constraint.getFirstType(), constraint.getSecondType(), matchKind, std::nullopt, constraint.getLocator()); } return simplifyBridgingConstraint(constraint.getFirstType(), constraint.getSecondType(), std::nullopt, constraint.getLocator()); case ConstraintKind::ApplicableFunction: return simplifyApplicableFnConstraint( constraint.getAppliedFunctionType(), constraint.getCalleeType(), constraint.getTrailingClosureMatching(), constraint.getDeclContext(), /*flags=*/std::nullopt, constraint.getLocator()); case ConstraintKind::DynamicCallableApplicableFunction: return simplifyDynamicCallableApplicableFnConstraint( constraint.getFirstType(), constraint.getSecondType(), std::nullopt, constraint.getLocator()); case ConstraintKind::DynamicTypeOf: return simplifyDynamicTypeOfConstraint( constraint.getFirstType(), constraint.getSecondType(), std::nullopt, constraint.getLocator()); case ConstraintKind::EscapableFunctionOf: return simplifyEscapableFunctionOfConstraint( constraint.getFirstType(), constraint.getSecondType(), std::nullopt, constraint.getLocator()); case ConstraintKind::OpenedExistentialOf: return simplifyOpenedExistentialOfConstraint( constraint.getFirstType(), constraint.getSecondType(), std::nullopt, constraint.getLocator()); case ConstraintKind::KeyPath: return simplifyKeyPathConstraint( constraint.getFirstType(), constraint.getSecondType(), constraint.getThirdType(), constraint.getTypeVariables(), std::nullopt, constraint.getLocator()); case ConstraintKind::KeyPathApplication: return simplifyKeyPathApplicationConstraint( constraint.getFirstType(), constraint.getSecondType(), constraint.getThirdType(), std::nullopt, constraint.getLocator()); case ConstraintKind::BindOverload: if (auto *fix = constraint.getFix()) { // TODO(diagnostics): Impact should be associated with a fix unless // it's a contextual problem, then only solver can decide what the impact // would be in each particular situation. auto impact = fix->getKind() == FixKind::AddQualifierToAccessTopLevelName ? 10 : 1; if (recordFix(fix, impact)) return SolutionKind::Error; } resolveOverload(constraint.getLocator(), constraint.getFirstType(), constraint.getOverloadChoice(), constraint.getDeclContext()); return SolutionKind::Solved; case ConstraintKind::SubclassOf: return simplifySubclassOfConstraint(constraint.getFirstType(), constraint.getSecondType(), constraint.getLocator(), /*flags*/ std::nullopt); case ConstraintKind::NonisolatedConformsTo: case ConstraintKind::ConformsTo: case ConstraintKind::LiteralConformsTo: return simplifyConformsToConstraint( constraint.getFirstType(), constraint.getSecondType(), constraint.getKind(), constraint.getLocator(), std::nullopt); case ConstraintKind::TransitivelyConformsTo: return simplifyTransitivelyConformsTo( constraint.getFirstType(), constraint.getSecondType(), constraint.getLocator(), std::nullopt); case ConstraintKind::CheckedCast: { auto result = simplifyCheckedCastConstraint( constraint.getFirstType(), constraint.getSecondType(), /*flags*/ std::nullopt, constraint.getLocator()); // NOTE: simplifyCheckedCastConstraint() may return Unsolved, e.g. if the // subexpression's type is unresolved. Don't record the fix until we // successfully simplify the constraint. if (result == SolutionKind::Solved) { if (auto *fix = constraint.getFix()) { if (recordFix(fix)) { return SolutionKind::Error; } } } return result; } case ConstraintKind::OptionalObject: return simplifyOptionalObjectConstraint( constraint.getFirstType(), constraint.getSecondType(), /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: return simplifyMemberConstraint( constraint.getKind(), constraint.getFirstType(), constraint.getMember(), constraint.getSecondType(), constraint.getDeclContext(), constraint.getFunctionRefInfo(), /*outerAlternatives=*/{}, /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::ValueWitness: return simplifyValueWitnessConstraint( constraint.getKind(), constraint.getFirstType(), constraint.getRequirement(), constraint.getSecondType(), constraint.getDeclContext(), constraint.getFunctionRefInfo(), /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::Defaultable: return simplifyDefaultableConstraint( constraint.getFirstType(), constraint.getSecondType(), /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::FallbackType: return simplifyFallbackTypeConstraint( constraint.getFirstType(), constraint.getSecondType(), constraint.getTypeVariables(), /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::PropertyWrapper: return simplifyPropertyWrapperConstraint( constraint.getFirstType(), constraint.getSecondType(), /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::Disjunction: case ConstraintKind::Conjunction: // See {Dis, Con}junctionStep class in CSStep.cpp for solving return SolutionKind::Unsolved; case ConstraintKind::OneWayEqual: return simplifyOneWayConstraint( constraint.getKind(), constraint.getFirstType(), constraint.getSecondType(), /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::UnresolvedMemberChainBase: return simplifyUnresolvedMemberChainBaseConstraint( constraint.getFirstType(), constraint.getSecondType(), /*flags=*/std::nullopt, constraint.getLocator()); case ConstraintKind::SyntacticElement: return simplifySyntacticElementConstraint( constraint.getSyntacticElement(), constraint.getElementContext(), constraint.isDiscardedElement(), /*flags=*/std::nullopt, constraint.getLocator()); case ConstraintKind::BindTupleOfFunctionParams: return simplifyBindTupleOfFunctionParamsConstraint( constraint.getFirstType(), constraint.getSecondType(), /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::PackElementOf: return simplifyPackElementOfConstraint( constraint.getFirstType(), constraint.getSecondType(), /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::ShapeOf: return simplifyShapeOfConstraint( constraint.getFirstType(), constraint.getSecondType(), /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::SameShape: return simplifySameShapeConstraint( constraint.getFirstType(), constraint.getSecondType(), /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::ExplicitGenericArguments: return simplifyExplicitGenericArgumentsConstraint( constraint.getFirstType(), constraint.getSecondType(), /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::MaterializePackExpansion: return simplifyMaterializePackExpansionConstraint( constraint.getFirstType(), constraint.getSecondType(), /*flags*/ std::nullopt, constraint.getLocator()); case ConstraintKind::LValueObject: return simplifyLValueObjectConstraint( constraint.getFirstType(), constraint.getSecondType(), /*flags*/ std::nullopt, constraint.getLocator()); } llvm_unreachable("Unhandled ConstraintKind in switch."); } void ConstraintSystem::simplifyDisjunctionChoice(Constraint *choice) { // Simplify this term in the disjunction. switch (simplifyConstraint(*choice)) { case ConstraintSystem::SolutionKind::Error: recordFailedConstraint(choice); break; case ConstraintSystem::SolutionKind::Solved: break; case ConstraintSystem::SolutionKind::Unsolved: addUnsolvedConstraint(choice); break; } }