GenericEnvironment::GenericEnvironment( TypeSubstitutionMap interfaceToArchetypeMap) { assert(!interfaceToArchetypeMap.empty()); // Build a mapping in both directions, making sure to canonicalize the // interface type where it is used as a key, so that substitution can // find them, and to preserve sugar otherwise, so that // mapTypeOutOfContext() produces a human-readable type. for (auto entry : interfaceToArchetypeMap) { // We're going to pass InterfaceToArchetypeMap to Type::subst(), which // expects the keys to be canonical, otherwise it won't be able to // find them. auto canParamTy = cast<GenericTypeParamType>(entry.first->getCanonicalType()); auto contextTy = entry.second; auto result = InterfaceToArchetypeMap.insert( std::make_pair(canParamTy, contextTy)); assert(result.second && "duplicate generic parameters in environment"); // If we mapped the generic parameter to an archetype, add it to the // reverse mapping. if (auto *archetypeTy = entry.second->getAs<ArchetypeType>()) ArchetypeToInterfaceMap[archetypeTy] = entry.first; // FIXME: If multiple generic parameters map to the same archetype, // the reverse mapping order is not deterministic. } }
/// Determine how close an argument list is to an already decomposed argument /// list. If the closeness is a miss by a single argument, then this returns /// information about that failure. CalleeCandidateInfo::ClosenessResultTy CalleeCandidateInfo::evaluateCloseness(UncurriedCandidate candidate, ArrayRef<AnyFunctionType::Param> actualArgs) { auto *dc = candidate.getDecl() ? candidate.getDecl()->getInnermostDeclContext() : nullptr; if (!candidate.hasParameters()) return {CC_GeneralMismatch, {}}; auto candArgs = candidate.getParameters(); llvm::SmallBitVector candDefaultMap = computeDefaultMap(candArgs, candidate.getDecl(), candidate.level); struct OurListener : public MatchCallArgumentListener { CandidateCloseness result = CC_ExactMatch; public: CandidateCloseness getResult() const { return result; } void extraArgument(unsigned argIdx) override { result = CC_ArgumentCountMismatch; } void missingArgument(unsigned paramIdx) override { result = CC_ArgumentCountMismatch; } void missingLabel(unsigned paramIdx) override { result = CC_ArgumentLabelMismatch; } void outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx) override { result = CC_ArgumentLabelMismatch; } bool relabelArguments(ArrayRef<Identifier> newNames) override { result = CC_ArgumentLabelMismatch; return true; } } listener; // Use matchCallArguments to determine how close the argument list is (in // shape) to the specified candidates parameters. This ignores the concrete // types of the arguments, looking only at the argument labels etc. SmallVector<ParamBinding, 4> paramBindings; if (matchCallArguments(actualArgs, candArgs, candDefaultMap, hasTrailingClosure, /*allowFixes:*/ true, listener, paramBindings)) // On error, get our closeness from whatever problem the listener saw. return { listener.getResult(), {}}; // If we found a mapping, check to see if the matched up arguments agree in // their type and count the number of mismatched arguments. unsigned mismatchingArgs = 0; // Known mapping of archetypes in all arguments so far. An archetype may map // to another archetype if the constraint system substituted one for another. TypeSubstitutionMap allGenericSubstitutions; // Number of args of one generic archetype which are mismatched because // isSubstitutableFor() has failed. If all mismatches are of this type, we'll // return a different closeness for better diagnoses. Type nonSubstitutableArchetype = nullptr; unsigned nonSubstitutableArgs = 0; // The type of failure is that multiple occurrences of the same generic are // being passed arguments with different concrete types. bool genericWithDifferingConcreteTypes = false; // We classify an argument mismatch as being a "near" miss if it is a very // likely match due to a common sort of problem (e.g. wrong flags on a // function type, optional where none was expected, etc). This allows us to // heuristically filter large overload sets better. bool mismatchesAreNearMisses = true; CalleeCandidateInfo::FailedArgumentInfo failureInfo; // Local function which extracts type from the parameter container. auto getParamResultType = [](const AnyFunctionType::Param ¶m) -> Type { // If parameter is marked as @autoclosure, we are // only interested in it's resulting type. if (param.isAutoClosure()) { if (auto fnType = param.getType()->getAs<AnyFunctionType>()) return fnType->getResult(); } return param.getType(); }; for (unsigned i = 0, e = paramBindings.size(); i != e; ++i) { // Bindings specify the arguments that source the parameter. The only case // this returns a non-singular value is when there are varargs in play. auto &bindings = paramBindings[i]; auto param = candArgs[i]; auto paramType = getParamResultType(param); for (auto argNo : bindings) { auto argType = getParamResultType(actualArgs[argNo]); auto rArgType = argType->getRValueType(); // FIXME: Right now, a "matching" overload is one with a parameter whose // type is identical to the argument type, or substitutable via handling // of functions with primary archetypes in one or more parameters. // We can still do something more sophisticated with this. // FIXME: Use TC.isConvertibleTo? TypeSubstitutionMap archetypesMap; bool matched; if (paramType->hasUnresolvedType()) matched = true; else if (rArgType->hasTypeVariable() || rArgType->hasUnresolvedType()) matched = false; else { auto matchType = paramType; // If the parameter is an inout type, and we have a proper lvalue, match // against the type contained therein. if (param.isInOut() && argType->is<LValueType>()) matchType = matchType->getInOutObjectType(); if (candidate.substituted) { matchType.findIf([&](Type type) -> bool { // If the replacement is itself an archetype, then the constraint // system was asserting equivalencies between different levels of // generics, rather than binding a generic to a concrete type (and we // don't/won't have a concrete type). In which case, it is the // replacement we are interested in, since it is the one in our current // context. That generic type should equal itself. if (auto archetype = type->getAs<ArchetypeType>()) { archetypesMap[archetype] = archetype; } return false; }); } matched = findGenericSubstitutions(dc, matchType, rArgType, archetypesMap); } if (matched) { for (auto pair : archetypesMap) { auto archetype = pair.first->castTo<ArchetypeType>(); auto substitution = pair.second; auto existingSubstitution = allGenericSubstitutions[archetype]; if (!existingSubstitution) { // New substitution for this callee. allGenericSubstitutions[archetype] = substitution; // Not yet handling nested archetypes. if (!archetype->isPrimary()) return { CC_ArgumentMismatch, {}}; if (!isSubstitutableFor(substitution, archetype, CS.DC)) { // If we have multiple non-substitutable types, this is just a mismatched mess. if (!nonSubstitutableArchetype.isNull()) return { CC_ArgumentMismatch, {}}; if (auto argOptType = argType->getOptionalObjectType()) mismatchesAreNearMisses &= isSubstitutableFor(argOptType, archetype, CS.DC); else mismatchesAreNearMisses = false; nonSubstitutableArchetype = archetype; nonSubstitutableArgs = 1; matched = false; } } else { // Substitution for the same archetype as in a previous argument. bool isNonSubstitutableArchetype = !nonSubstitutableArchetype.isNull() && nonSubstitutableArchetype->isEqual(archetype); if (substitution->isEqual(existingSubstitution)) { if (isNonSubstitutableArchetype) { ++nonSubstitutableArgs; matched = false; } } else { // If we have only one nonSubstitutableArg so far, then this different // type might be the one that we should be substituting for instead. // Note that failureInfo is already set correctly for that case. if (isNonSubstitutableArchetype && nonSubstitutableArgs == 1 && isSubstitutableFor(substitution, archetype, CS.DC)) { mismatchesAreNearMisses = argumentMismatchIsNearMiss(existingSubstitution, substitution); allGenericSubstitutions[archetype] = substitution; } else { genericWithDifferingConcreteTypes = true; matched = false; } } } } } if (matched) continue; // If the real argument is unresolved, the candidate isn't a mismatch because // the type could be anything, but it's still useful to save the argument number as // failureInfo. if (rArgType->hasUnresolvedType() && !mismatchingArgs) { failureInfo.argumentNumber = argNo; } else { if (archetypesMap.empty()) mismatchesAreNearMisses &= argumentMismatchIsNearMiss(argType, paramType); ++mismatchingArgs; failureInfo.argumentNumber = argNo; failureInfo.parameterType = paramType; if (paramType->hasTypeParameter()) failureInfo.declContext = dc; } } } if (mismatchingArgs == 0) return { CC_ExactMatch, failureInfo}; // Check to see if the first argument expects an inout argument, but is not // an lvalue. if (candArgs[0].isInOut() && !(actualArgs[0].getType()->hasLValueType() || actualArgs[0].isInOut())) { return { CC_NonLValueInOut, {}}; } // If we have exactly one argument mismatching, classify it specially, so that // close matches are prioritized against obviously wrong ones. if (mismatchingArgs == 1) { CandidateCloseness closeness; if (allGenericSubstitutions.empty()) { closeness = mismatchesAreNearMisses ? CC_OneArgumentNearMismatch : CC_OneArgumentMismatch; } else { // If the failure is that different occurrences of the same generic have // different concrete types, substitute in all the concrete types we've found // into the failureInfo to improve diagnosis. if (genericWithDifferingConcreteTypes) { auto newType = failureInfo.parameterType.transform([&](Type type) -> Type { if (auto archetype = type->getAs<ArchetypeType>()) if (auto replacement = allGenericSubstitutions[archetype]) return replacement; return type; }); failureInfo.parameterType = newType; } closeness = mismatchesAreNearMisses ? CC_OneGenericArgumentNearMismatch : CC_OneGenericArgumentMismatch; } // Return information about the single failing argument. return { closeness, failureInfo }; } if (nonSubstitutableArgs == mismatchingArgs) return { CC_GenericNonsubstitutableMismatch, failureInfo }; auto closeness = mismatchesAreNearMisses ? CC_ArgumentNearMismatch : CC_ArgumentMismatch; return { closeness, {}}; }