Merge pull request #28600 from hborla/tuple-mismatch-diagnostics
[ConstraintSystem] Port tuple type mismatch diagnostics
This commit is contained in:
Коммит
edb4e70d66
|
@ -981,9 +981,6 @@ to the new diagnostic framework, which is described in detail in this
|
|||
|
||||
The things in the queue yet to be ported are:
|
||||
|
||||
- ``visitTupleExpr``: Diagnostics related to label/type mismatches
|
||||
associated with tuple conversions.
|
||||
|
||||
- Diagnostics related to member references: ``diagnoseMemberFailures``.
|
||||
Most of the associated diagnostics have been ported and fixes are
|
||||
located in ``ConstraintSystem::simplifyMemberConstraint``.
|
||||
|
|
|
@ -955,9 +955,8 @@ ERROR(types_not_convertible_use_bool_value,none,
|
|||
ERROR(tuple_types_not_convertible_nelts,none,
|
||||
"%0 is not convertible to %1, "
|
||||
"tuples have a different number of elements", (Type, Type))
|
||||
|
||||
ERROR(tuple_types_not_convertible,none,
|
||||
"tuple type %0 is not convertible to tuple %1", (Type, Type))
|
||||
"tuple type %0 is not convertible to tuple type %1", (Type, Type))
|
||||
|
||||
ERROR(invalid_force_unwrap,none,
|
||||
"cannot force unwrap value of non-optional type %0", (Type))
|
||||
|
|
|
@ -255,8 +255,7 @@ private:
|
|||
bool visitExpr(Expr *E);
|
||||
bool visitIdentityExpr(IdentityExpr *E);
|
||||
bool visitTryExpr(TryExpr *E);
|
||||
bool visitTupleExpr(TupleExpr *E);
|
||||
|
||||
|
||||
bool visitUnresolvedMemberExpr(UnresolvedMemberExpr *E);
|
||||
bool visitUnresolvedDotExpr(UnresolvedDotExpr *UDE);
|
||||
bool visitArrayExpr(ArrayExpr *E);
|
||||
|
@ -2909,58 +2908,6 @@ bool FailureDiagnosis::visitUnresolvedDotExpr(UnresolvedDotExpr *UDE) {
|
|||
locator);
|
||||
}
|
||||
|
||||
/// A TupleExpr propagate contextual type information down to its children and
|
||||
/// can be erroneous when there is a label mismatch etc.
|
||||
bool FailureDiagnosis::visitTupleExpr(TupleExpr *TE) {
|
||||
// If we know the requested argType to use, use computeTupleShuffle to produce
|
||||
// the shuffle of input arguments to destination values. It requires a
|
||||
// TupleType to compute the mapping from argExpr. Conveniently, it doesn't
|
||||
// care about the actual types though, so we can just use 'void' for them.
|
||||
if (!CS.getContextualType() || !CS.getContextualType()->is<TupleType>())
|
||||
return visitExpr(TE);
|
||||
|
||||
auto contextualTT = CS.getContextualType()->castTo<TupleType>();
|
||||
|
||||
SmallVector<TupleTypeElt, 4> ArgElts;
|
||||
auto voidTy = CS.getASTContext().TheEmptyTupleType;
|
||||
|
||||
for (unsigned i = 0, e = TE->getNumElements(); i != e; ++i)
|
||||
ArgElts.push_back({ voidTy, TE->getElementName(i) });
|
||||
auto TEType = TupleType::get(ArgElts, CS.getASTContext());
|
||||
|
||||
if (!TEType->is<TupleType>())
|
||||
return visitExpr(TE);
|
||||
|
||||
SmallVector<unsigned, 4> sources;
|
||||
|
||||
// If the shuffle is invalid, then there is a type error. We could diagnose
|
||||
// it specifically here, but the general logic does a fine job so we let it
|
||||
// do it.
|
||||
if (computeTupleShuffle(TEType->castTo<TupleType>()->getElements(),
|
||||
contextualTT->getElements(), sources))
|
||||
return visitExpr(TE);
|
||||
|
||||
// If we got a correct shuffle, we can perform the analysis of all of
|
||||
// the input elements, with their expected types.
|
||||
for (unsigned i = 0, e = sources.size(); i != e; ++i) {
|
||||
// Otherwise, it must match the corresponding expected argument type.
|
||||
unsigned inArgNo = sources[i];
|
||||
|
||||
TCCOptions options;
|
||||
if (contextualTT->getElement(i).isInOut())
|
||||
options |= TCC_AllowLValue;
|
||||
|
||||
auto actualType = contextualTT->getElementType(i);
|
||||
auto exprResult =
|
||||
typeCheckChildIndependently(TE->getElement(inArgNo), actualType,
|
||||
CS.getContextualTypePurpose(), options);
|
||||
// If there was an error type checking this argument, then we're done.
|
||||
if (!exprResult) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// An IdentityExpr doesn't change its argument, but it *can* propagate its
|
||||
/// contextual type information down.
|
||||
bool FailureDiagnosis::visitIdentityExpr(IdentityExpr *E) {
|
||||
|
|
|
@ -137,165 +137,6 @@ FailureDiagnostic::getChoiceFor(ConstraintLocator *locator) const {
|
|||
return getOverloadChoiceIfAvailable(cs.getCalleeLocator(locator));
|
||||
}
|
||||
|
||||
Type FailureDiagnostic::resolveInterfaceType(Type type,
|
||||
bool reconstituteSugar) const {
|
||||
auto &cs = getConstraintSystem();
|
||||
auto resolvedType = type.transform([&](Type type) -> Type {
|
||||
if (auto *tvt = type->getAs<TypeVariableType>()) {
|
||||
// If this type variable is for a generic parameter, return that.
|
||||
if (auto *gp = tvt->getImpl().getGenericParameter())
|
||||
return gp;
|
||||
|
||||
// Otherwise resolve its fixed type, mapped out of context.
|
||||
if (auto fixed = cs.getFixedType(tvt))
|
||||
return resolveInterfaceType(fixed->mapTypeOutOfContext());
|
||||
|
||||
return cs.getRepresentative(tvt);
|
||||
}
|
||||
if (auto *dmt = type->getAs<DependentMemberType>()) {
|
||||
// For a dependent member, first resolve the base.
|
||||
auto newBase = resolveInterfaceType(dmt->getBase());
|
||||
|
||||
// Then reconstruct using its associated type.
|
||||
assert(dmt->getAssocType());
|
||||
return DependentMemberType::get(newBase, dmt->getAssocType());
|
||||
}
|
||||
return type;
|
||||
});
|
||||
|
||||
assert(!resolvedType->hasArchetype());
|
||||
return reconstituteSugar ? resolvedType->reconstituteSugar(/*recursive*/ true)
|
||||
: resolvedType;
|
||||
}
|
||||
|
||||
/// Given an apply expr, returns true if it is expected to have a direct callee
|
||||
/// overload, resolvable using `getChoiceFor`. Otherwise, returns false.
|
||||
static bool shouldHaveDirectCalleeOverload(const CallExpr *callExpr) {
|
||||
auto *fnExpr = callExpr->getDirectCallee();
|
||||
|
||||
// An apply of an apply/subscript doesn't have a direct callee.
|
||||
if (isa<ApplyExpr>(fnExpr) || isa<SubscriptExpr>(fnExpr))
|
||||
return false;
|
||||
|
||||
// Applies of closures don't have callee overloads.
|
||||
if (isa<ClosureExpr>(fnExpr))
|
||||
return false;
|
||||
|
||||
// No direct callee for a try!/try?.
|
||||
if (isa<ForceTryExpr>(fnExpr) || isa<OptionalTryExpr>(fnExpr))
|
||||
return false;
|
||||
|
||||
// If we have an intermediate cast, there's no direct callee.
|
||||
if (isa<ExplicitCastExpr>(fnExpr))
|
||||
return false;
|
||||
|
||||
// No direct callee for an if expr.
|
||||
if (isa<IfExpr>(fnExpr))
|
||||
return false;
|
||||
|
||||
// Assume that anything else would have a direct callee.
|
||||
return true;
|
||||
}
|
||||
|
||||
Optional<FunctionArgApplyInfo>
|
||||
FailureDiagnostic::getFunctionArgApplyInfo(ConstraintLocator *locator) const {
|
||||
auto &cs = getConstraintSystem();
|
||||
auto *anchor = locator->getAnchor();
|
||||
auto path = locator->getPath();
|
||||
|
||||
// Look for the apply-arg-to-param element in the locator's path. We may
|
||||
// have to look through other elements that are generated from an argument
|
||||
// conversion such as GenericArgument for an optional-to-optional conversion,
|
||||
// and OptionalPayload for a value-to-optional conversion.
|
||||
auto iter = path.rbegin();
|
||||
auto applyArgElt = locator->findLast<LocatorPathElt::ApplyArgToParam>(iter);
|
||||
if (!applyArgElt)
|
||||
return None;
|
||||
|
||||
auto nextIter = iter + 1;
|
||||
assert(!locator->findLast<LocatorPathElt::ApplyArgToParam>(nextIter) &&
|
||||
"Multiple ApplyArgToParam components?");
|
||||
|
||||
// Form a new locator that ends at the apply-arg-to-param element, and
|
||||
// simplify it to get the full argument expression.
|
||||
auto argPath = path.drop_back(iter - path.rbegin());
|
||||
auto *argLocator = cs.getConstraintLocator(
|
||||
anchor, argPath, ConstraintLocator::getSummaryFlagsForPath(argPath));
|
||||
|
||||
auto *argExpr = simplifyLocatorToAnchor(argLocator);
|
||||
|
||||
// If we were unable to simplify down to the argument expression, we don't
|
||||
// know what this is.
|
||||
if (!argExpr)
|
||||
return None;
|
||||
|
||||
Optional<OverloadChoice> choice;
|
||||
Type rawFnType;
|
||||
if (auto overload = getChoiceFor(argLocator)) {
|
||||
// If we have resolved an overload for the callee, then use that to get the
|
||||
// function type and callee.
|
||||
choice = overload->choice;
|
||||
rawFnType = overload->openedType;
|
||||
} else {
|
||||
// If we didn't resolve an overload for the callee, we should be dealing
|
||||
// with a call of an arbitrary function expr.
|
||||
if (auto *call = dyn_cast<CallExpr>(anchor)) {
|
||||
assert(!shouldHaveDirectCalleeOverload(call) &&
|
||||
"Should we have resolved a callee for this?");
|
||||
rawFnType = cs.getType(call->getFn());
|
||||
} else {
|
||||
// FIXME: ArgumentMismatchFailure is currently used from CSDiag, meaning
|
||||
// we can end up a BinaryExpr here with an unresolved callee. It should be
|
||||
// possible to remove this once we've gotten rid of the old CSDiag logic
|
||||
// and just assert that we have a CallExpr.
|
||||
auto *apply = cast<ApplyExpr>(anchor);
|
||||
rawFnType = cs.getType(apply->getFn());
|
||||
}
|
||||
}
|
||||
|
||||
// Try to resolve the function type by loading lvalues and looking through
|
||||
// optional types, which can occur for expressions like `fn?(5)`.
|
||||
auto *fnType = resolveType(rawFnType)
|
||||
->lookThroughAllOptionalTypes()
|
||||
->getAs<FunctionType>();
|
||||
if (!fnType)
|
||||
return None;
|
||||
|
||||
// Resolve the interface type for the function. Note that this may not be a
|
||||
// function type, for example it could be a generic parameter.
|
||||
Type fnInterfaceType;
|
||||
auto *callee = choice ? choice->getDeclOrNull() : nullptr;
|
||||
if (callee && callee->hasInterfaceType()) {
|
||||
// If we have a callee with an interface type, we can use it. This is
|
||||
// preferable to resolveInterfaceType, as this will allow us to get a
|
||||
// GenericFunctionType for generic decls.
|
||||
//
|
||||
// Note that it's possible to find a callee without an interface type. This
|
||||
// can happen for example with closure parameters, where the interface type
|
||||
// isn't set until the solution is applied. In that case, use
|
||||
// resolveInterfaceType.
|
||||
fnInterfaceType = callee->getInterfaceType();
|
||||
|
||||
// Strip off the curried self parameter if necessary.
|
||||
if (hasAppliedSelf(cs, *choice))
|
||||
fnInterfaceType = fnInterfaceType->castTo<AnyFunctionType>()->getResult();
|
||||
|
||||
if (auto *fn = fnInterfaceType->getAs<AnyFunctionType>()) {
|
||||
assert(fn->getNumParams() == fnType->getNumParams() &&
|
||||
"Parameter mismatch?");
|
||||
(void)fn;
|
||||
}
|
||||
} else {
|
||||
fnInterfaceType = resolveInterfaceType(rawFnType);
|
||||
}
|
||||
|
||||
auto argIdx = applyArgElt->getArgIdx();
|
||||
auto paramIdx = applyArgElt->getParamIdx();
|
||||
|
||||
return FunctionArgApplyInfo(cs, argExpr, argIdx, getType(argExpr),
|
||||
paramIdx, fnInterfaceType, fnType, callee);
|
||||
}
|
||||
|
||||
Type FailureDiagnostic::restoreGenericParameters(
|
||||
Type type,
|
||||
llvm::function_ref<void(GenericTypeParamType *, Type)> substitution) {
|
||||
|
@ -935,7 +776,8 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const {
|
|||
// Let's check whether this is a function parameter passed
|
||||
// as an argument to another function which accepts @escaping
|
||||
// function at that position.
|
||||
if (auto argApplyInfo = getFunctionArgApplyInfo(getLocator())) {
|
||||
auto &cs = getConstraintSystem();
|
||||
if (auto argApplyInfo = cs.getFunctionArgApplyInfo(getLocator())) {
|
||||
auto paramInterfaceTy = argApplyInfo->getParamInterfaceType();
|
||||
if (paramInterfaceTy->isTypeParameter()) {
|
||||
auto diagnoseGenericParamFailure = [&](GenericTypeParamDecl *decl) {
|
||||
|
@ -1143,7 +985,8 @@ void MissingOptionalUnwrapFailure::offerDefaultValueUnwrapFixIt(
|
|||
if (isa<InOutExpr>(anchor))
|
||||
return;
|
||||
|
||||
if (auto argApplyInfo = getFunctionArgApplyInfo(getLocator()))
|
||||
auto &cs = getConstraintSystem();
|
||||
if (auto argApplyInfo = cs.getFunctionArgApplyInfo(getLocator()))
|
||||
if (argApplyInfo->getParameterFlags().isInOut())
|
||||
return;
|
||||
|
||||
|
@ -2967,9 +2810,18 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context,
|
|||
}
|
||||
|
||||
bool TupleContextualFailure::diagnoseAsError() {
|
||||
auto diagnostic = isNumElementsMismatch()
|
||||
? diag::tuple_types_not_convertible_nelts
|
||||
: diag::tuple_types_not_convertible;
|
||||
Diag<Type, Type> diagnostic;
|
||||
auto purpose = getContextualTypePurpose();
|
||||
auto &cs = getConstraintSystem();
|
||||
if (isNumElementsMismatch())
|
||||
diagnostic = diag::tuple_types_not_convertible_nelts;
|
||||
else if ((purpose == CTP_Initialization) && !cs.getContextualType())
|
||||
diagnostic = diag::tuple_types_not_convertible;
|
||||
else if (auto diag = getDiagnosticFor(purpose, /*forProtocol=*/false))
|
||||
diagnostic = *diag;
|
||||
else
|
||||
return false;
|
||||
|
||||
emitDiagnostic(getAnchor()->getLoc(), diagnostic, getFromType(), getToType());
|
||||
return true;
|
||||
}
|
||||
|
@ -3825,7 +3677,7 @@ bool MissingArgumentsFailure::diagnoseAsError() {
|
|||
// foo(bar) // `() -> Void` vs. `(Int) -> Void`
|
||||
// ```
|
||||
if (locator->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
|
||||
auto info = *getFunctionArgApplyInfo(locator);
|
||||
auto info = *(cs.getFunctionArgApplyInfo(locator));
|
||||
|
||||
auto *argExpr = info.getArgExpr();
|
||||
emitDiagnostic(argExpr->getLoc(), diag::cannot_convert_argument_value,
|
||||
|
@ -4041,7 +3893,7 @@ bool MissingArgumentsFailure::diagnoseClosure(ClosureExpr *closure) {
|
|||
auto *locator = getLocator();
|
||||
if (locator->isForContextualType()) {
|
||||
funcType = cs.getContextualType()->getAs<FunctionType>();
|
||||
} else if (auto info = getFunctionArgApplyInfo(locator)) {
|
||||
} else if (auto info = cs.getFunctionArgApplyInfo(locator)) {
|
||||
funcType = info->getParamType()->getAs<FunctionType>();
|
||||
} else if (locator->isLastElement<LocatorPathElt::ClosureResult>()) {
|
||||
// Based on the locator we know this this is something like this:
|
||||
|
@ -4754,7 +4606,8 @@ SourceLoc InvalidUseOfAddressOf::getLoc() const {
|
|||
}
|
||||
|
||||
bool InvalidUseOfAddressOf::diagnoseAsError() {
|
||||
if (auto argApplyInfo = getFunctionArgApplyInfo(getLocator())) {
|
||||
auto &cs = getConstraintSystem();
|
||||
if (auto argApplyInfo = cs.getFunctionArgApplyInfo(getLocator())) {
|
||||
if (!argApplyInfo->getParameterFlags().isInOut()) {
|
||||
auto anchor = getAnchor();
|
||||
emitDiagnostic(anchor->getLoc(), diag::extra_address_of, getToType())
|
||||
|
@ -5274,18 +5127,19 @@ bool ThrowingFunctionConversionFailure::diagnoseAsError() {
|
|||
}
|
||||
|
||||
bool InOutConversionFailure::diagnoseAsError() {
|
||||
auto &cs = getConstraintSystem();
|
||||
auto *anchor = getAnchor();
|
||||
auto *locator = getLocator();
|
||||
auto path = locator->getPath();
|
||||
|
||||
if (!path.empty() &&
|
||||
path.back().getKind() == ConstraintLocator::FunctionArgument) {
|
||||
if (auto argApplyInfo = getFunctionArgApplyInfo(locator)) {
|
||||
if (auto argApplyInfo = cs.getFunctionArgApplyInfo(locator)) {
|
||||
emitDiagnostic(anchor->getLoc(), diag::cannot_convert_argument_value,
|
||||
argApplyInfo->getArgType(), argApplyInfo->getParamType());
|
||||
} else {
|
||||
assert(locator->findLast<LocatorPathElt::ContextualType>());
|
||||
auto contextualType = getConstraintSystem().getContextualType();
|
||||
auto contextualType = cs.getContextualType();
|
||||
auto purpose = getContextualTypePurpose();
|
||||
auto diagnostic = getDiagnosticFor(purpose, /*forProtocol=*/false);
|
||||
|
||||
|
|
|
@ -113,10 +113,6 @@ public:
|
|||
});
|
||||
}
|
||||
|
||||
/// Resolve type variables present in the raw type, using generic parameter
|
||||
/// types where possible.
|
||||
Type resolveInterfaceType(Type type, bool reconstituteSugar = false) const;
|
||||
|
||||
template <typename... ArgTypes>
|
||||
InFlightDiagnostic emitDiagnostic(ArgTypes &&... Args) const;
|
||||
|
||||
|
@ -176,13 +172,6 @@ protected:
|
|||
/// of a given locator's anchor, or \c None if no such choice can be found.
|
||||
Optional<SelectedOverload> getChoiceFor(ConstraintLocator *) const;
|
||||
|
||||
/// For a given locator describing a function argument conversion, or a
|
||||
/// constraint within an argument conversion, returns information about the
|
||||
/// application of the argument to its parameter. If the locator is not
|
||||
/// for an argument conversion, returns \c None.
|
||||
Optional<FunctionArgApplyInfo>
|
||||
getFunctionArgApplyInfo(ConstraintLocator *locator) const;
|
||||
|
||||
/// \returns A new type with all of the type variables associated with
|
||||
/// generic parameters substituted back into being generic parameter type.
|
||||
Type restoreGenericParameters(
|
||||
|
@ -195,142 +184,6 @@ private:
|
|||
std::pair<Expr *, bool> computeAnchor() const;
|
||||
};
|
||||
|
||||
/// Provides information about the application of a function argument to a
|
||||
/// parameter.
|
||||
class FunctionArgApplyInfo {
|
||||
ConstraintSystem &CS;
|
||||
Expr *ArgExpr;
|
||||
unsigned ArgIdx;
|
||||
Type ArgType;
|
||||
|
||||
unsigned ParamIdx;
|
||||
|
||||
Type FnInterfaceType;
|
||||
FunctionType *FnType;
|
||||
const ValueDecl *Callee;
|
||||
|
||||
public:
|
||||
FunctionArgApplyInfo(ConstraintSystem &cs, Expr *argExpr, unsigned argIdx,
|
||||
Type argType, unsigned paramIdx, Type fnInterfaceType,
|
||||
FunctionType *fnType, const ValueDecl *callee)
|
||||
: CS(cs), ArgExpr(argExpr), ArgIdx(argIdx), ArgType(argType),
|
||||
ParamIdx(paramIdx), FnInterfaceType(fnInterfaceType), FnType(fnType),
|
||||
Callee(callee) {}
|
||||
|
||||
/// \returns The argument being applied.
|
||||
Expr *getArgExpr() const { return ArgExpr; }
|
||||
|
||||
/// \returns The position of the argument, starting at 1.
|
||||
unsigned getArgPosition() const { return ArgIdx + 1; }
|
||||
|
||||
/// \returns The position of the parameter, starting at 1.
|
||||
unsigned getParamPosition() const { return ParamIdx + 1; }
|
||||
|
||||
/// \returns The type of the argument being applied, including any generic
|
||||
/// substitutions.
|
||||
///
|
||||
/// \param withSpecifier Whether to keep the inout or @lvalue specifier of
|
||||
/// the argument, if any.
|
||||
Type getArgType(bool withSpecifier = false) const {
|
||||
return withSpecifier ? ArgType : ArgType->getWithoutSpecifierType();
|
||||
}
|
||||
|
||||
/// \returns The label for the argument being applied.
|
||||
Identifier getArgLabel() const {
|
||||
auto *parent = CS.getParentExpr(ArgExpr);
|
||||
if (auto *te = dyn_cast<TupleExpr>(parent))
|
||||
return te->getElementName(ArgIdx);
|
||||
|
||||
assert(isa<ParenExpr>(parent));
|
||||
return Identifier();
|
||||
}
|
||||
|
||||
/// \returns A textual description of the argument suitable for diagnostics.
|
||||
/// For an argument with an unambiguous label, this will the label. Otherwise
|
||||
/// it will be its position in the argument list.
|
||||
StringRef getArgDescription(SmallVectorImpl<char> &scratch) const {
|
||||
llvm::raw_svector_ostream stream(scratch);
|
||||
|
||||
// Use the argument label only if it's unique within the argument list.
|
||||
auto argLabel = getArgLabel();
|
||||
auto useArgLabel = [&]() -> bool {
|
||||
if (argLabel.empty())
|
||||
return false;
|
||||
|
||||
if (auto *te = dyn_cast<TupleExpr>(CS.getParentExpr(ArgExpr)))
|
||||
return llvm::count(te->getElementNames(), argLabel) == 1;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (useArgLabel()) {
|
||||
stream << "'";
|
||||
stream << argLabel;
|
||||
stream << "'";
|
||||
} else {
|
||||
stream << "#";
|
||||
stream << getArgPosition();
|
||||
}
|
||||
return StringRef(scratch.data(), scratch.size());
|
||||
}
|
||||
|
||||
/// \returns The interface type for the function being applied. Note that this
|
||||
/// may not a function type, for example it could be a generic parameter.
|
||||
Type getFnInterfaceType() const { return FnInterfaceType; }
|
||||
|
||||
/// \returns The function type being applied, including any generic
|
||||
/// substitutions.
|
||||
FunctionType *getFnType() const { return FnType; }
|
||||
|
||||
/// \returns The callee for the application.
|
||||
const ValueDecl *getCallee() const { return Callee; }
|
||||
|
||||
private:
|
||||
Type getParamTypeImpl(AnyFunctionType *fnTy,
|
||||
bool lookThroughAutoclosure) const {
|
||||
auto param = fnTy->getParams()[ParamIdx];
|
||||
auto paramTy = param.getPlainType();
|
||||
if (lookThroughAutoclosure && param.isAutoClosure())
|
||||
paramTy = paramTy->castTo<FunctionType>()->getResult();
|
||||
return paramTy;
|
||||
}
|
||||
|
||||
public:
|
||||
/// \returns The type of the parameter which the argument is being applied to,
|
||||
/// including any generic substitutions.
|
||||
///
|
||||
/// \param lookThroughAutoclosure Whether an @autoclosure () -> T parameter
|
||||
/// should be treated as being of type T.
|
||||
Type getParamType(bool lookThroughAutoclosure = true) const {
|
||||
return getParamTypeImpl(FnType, lookThroughAutoclosure);
|
||||
}
|
||||
|
||||
/// \returns The interface type of the parameter which the argument is being
|
||||
/// applied to.
|
||||
///
|
||||
/// \param lookThroughAutoclosure Whether an @autoclosure () -> T parameter
|
||||
/// should be treated as being of type T.
|
||||
Type getParamInterfaceType(bool lookThroughAutoclosure = true) const {
|
||||
auto interfaceFnTy = FnInterfaceType->getAs<AnyFunctionType>();
|
||||
if (!interfaceFnTy) {
|
||||
// If the interface type isn't a function, then just return the resolved
|
||||
// parameter type.
|
||||
return getParamType(lookThroughAutoclosure)->mapTypeOutOfContext();
|
||||
}
|
||||
return getParamTypeImpl(interfaceFnTy, lookThroughAutoclosure);
|
||||
}
|
||||
|
||||
/// \returns The flags of the parameter which the argument is being applied
|
||||
/// to.
|
||||
ParameterTypeFlags getParameterFlags() const {
|
||||
return FnType->getParams()[ParamIdx].getParameterFlags();
|
||||
}
|
||||
|
||||
ParameterTypeFlags getParameterFlagsAtIndex(unsigned idx) const {
|
||||
return FnType->getParams()[idx].getParameterFlags();
|
||||
}
|
||||
};
|
||||
|
||||
/// Base class for all of the diagnostics related to generic requirement
|
||||
/// failures, provides common information like failed requirement,
|
||||
/// declaration where such requirement comes from, etc.
|
||||
|
@ -977,17 +830,24 @@ protected:
|
|||
|
||||
/// Diagnose mismatches relating to tuple destructuring.
|
||||
class TupleContextualFailure final : public ContextualFailure {
|
||||
/// Indices of the tuple elements whose types do not match.
|
||||
llvm::SmallVector<unsigned, 4> Indices;
|
||||
|
||||
public:
|
||||
TupleContextualFailure(ConstraintSystem &cs, Type lhs, Type rhs,
|
||||
TupleContextualFailure(ConstraintSystem &cs, ContextualTypePurpose purpose,
|
||||
Type lhs, Type rhs, llvm::ArrayRef<unsigned> indices,
|
||||
ConstraintLocator *locator)
|
||||
: ContextualFailure(cs, lhs, rhs, locator) {}
|
||||
: ContextualFailure(cs, purpose, lhs, rhs, locator),
|
||||
Indices(indices.begin(), indices.end()) {
|
||||
std::sort(Indices.begin(), Indices.end());
|
||||
assert(getFromType()->is<TupleType>() && getToType()->is<TupleType>());
|
||||
}
|
||||
|
||||
bool diagnoseAsError() override;
|
||||
|
||||
bool isNumElementsMismatch() const {
|
||||
auto lhsTy = getFromType()->castTo<TupleType>();
|
||||
auto rhsTy = getToType()->castTo<TupleType>();
|
||||
assert(lhsTy && rhsTy);
|
||||
return lhsTy->getNumElements() != rhsTy->getNumElements();
|
||||
}
|
||||
};
|
||||
|
@ -1825,7 +1685,7 @@ public:
|
|||
ArgumentMismatchFailure(ConstraintSystem &cs, Type argType,
|
||||
Type paramType, ConstraintLocator *locator)
|
||||
: ContextualFailure(cs, argType, paramType, locator),
|
||||
Info(getFunctionArgApplyInfo(getLocator())) {}
|
||||
Info(cs.getFunctionArgApplyInfo(getLocator())) {}
|
||||
|
||||
bool diagnoseAsError() override;
|
||||
bool diagnoseAsNote() override;
|
||||
|
|
|
@ -255,18 +255,64 @@ ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs,
|
|||
return new (cs.getAllocator()) ContextualMismatch(cs, lhs, rhs, locator);
|
||||
}
|
||||
|
||||
bool AllowTupleTypeMismatch::diagnose(bool asNote) const {
|
||||
auto failure = TupleContextualFailure(
|
||||
getConstraintSystem(), getFromType(), getToType(), getLocator());
|
||||
bool AllowTupleTypeMismatch::coalesceAndDiagnose(
|
||||
ArrayRef<ConstraintFix *> fixes, bool asNote) const {
|
||||
llvm::SmallVector<unsigned, 4> indices;
|
||||
if (isElementMismatch())
|
||||
indices.push_back(*Index);
|
||||
|
||||
for (auto fix : fixes) {
|
||||
auto *tupleFix = fix->getAs<AllowTupleTypeMismatch>();
|
||||
if (!tupleFix || !tupleFix->isElementMismatch())
|
||||
continue;
|
||||
indices.push_back(*tupleFix->Index);
|
||||
}
|
||||
|
||||
auto &cs = getConstraintSystem();
|
||||
auto *locator = getLocator();
|
||||
auto purpose = cs.getContextualTypePurpose();
|
||||
Type fromType;
|
||||
Type toType;
|
||||
|
||||
if (getFromType()->is<TupleType>() && getToType()->is<TupleType>()) {
|
||||
fromType = getFromType();
|
||||
toType = getToType();
|
||||
} else if (auto contextualType = cs.getContextualType()) {
|
||||
auto *tupleExpr = simplifyLocatorToAnchor(locator);
|
||||
if (!tupleExpr)
|
||||
return false;
|
||||
fromType = cs.getType(tupleExpr);
|
||||
toType = contextualType;
|
||||
} else if (auto argApplyInfo = cs.getFunctionArgApplyInfo(locator)) {
|
||||
purpose = CTP_CallArgument;
|
||||
fromType = argApplyInfo->getArgType();
|
||||
toType = argApplyInfo->getParamType();
|
||||
} else if (auto *coerceExpr = dyn_cast<CoerceExpr>(locator->getAnchor())) {
|
||||
purpose = CTP_CoerceOperand;
|
||||
fromType = cs.getType(coerceExpr->getSubExpr());
|
||||
toType = cs.getType(coerceExpr);
|
||||
} else if (auto *assignExpr = dyn_cast<AssignExpr>(locator->getAnchor())) {
|
||||
purpose = CTP_AssignSource;
|
||||
fromType = cs.getType(assignExpr->getSrc());
|
||||
toType = cs.getType(assignExpr->getDest());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
TupleContextualFailure failure(cs, purpose, fromType, toType, indices, locator);
|
||||
return failure.diagnose(asNote);
|
||||
}
|
||||
|
||||
bool AllowTupleTypeMismatch::diagnose(bool asNote) const {
|
||||
return coalesceAndDiagnose({}, asNote);
|
||||
}
|
||||
|
||||
AllowTupleTypeMismatch *
|
||||
AllowTupleTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs,
|
||||
ConstraintLocator *locator) {
|
||||
assert(lhs->is<TupleType>() && rhs->is<TupleType>() &&
|
||||
"lhs and rhs must be tuple types");
|
||||
return new (cs.getAllocator()) AllowTupleTypeMismatch(cs, lhs, rhs, locator);
|
||||
ConstraintLocator *locator,
|
||||
Optional<unsigned> index) {
|
||||
return new (cs.getAllocator())
|
||||
AllowTupleTypeMismatch(cs, lhs, rhs, locator, index);
|
||||
}
|
||||
|
||||
bool GenericArgumentsMismatch::diagnose(bool asNote) const {
|
||||
|
|
|
@ -954,19 +954,36 @@ private:
|
|||
};
|
||||
|
||||
class AllowTupleTypeMismatch final : public ContextualMismatch {
|
||||
/// If this is an element mismatch, \c Index is the element index where the
|
||||
/// type mismatch occurred. If this is an arity or label mismatch, \c Index
|
||||
/// will be \c None.
|
||||
Optional<unsigned> Index;
|
||||
|
||||
AllowTupleTypeMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
|
||||
ConstraintLocator *locator)
|
||||
ConstraintLocator *locator, Optional<unsigned> index)
|
||||
: ContextualMismatch(cs, FixKind::AllowTupleTypeMismatch, lhs, rhs,
|
||||
locator) {}
|
||||
locator), Index(index) {}
|
||||
|
||||
public:
|
||||
static AllowTupleTypeMismatch *create(ConstraintSystem &cs, Type lhs,
|
||||
Type rhs, ConstraintLocator *locator);
|
||||
Type rhs, ConstraintLocator *locator,
|
||||
Optional<unsigned> index = None);
|
||||
|
||||
static bool classof(const ConstraintFix *fix) {
|
||||
return fix->getKind() == FixKind::AllowTupleTypeMismatch;
|
||||
}
|
||||
|
||||
std::string getName() const override {
|
||||
return "fix tuple mismatches in type and arity";
|
||||
}
|
||||
|
||||
bool isElementMismatch() const {
|
||||
return Index.hasValue();
|
||||
}
|
||||
|
||||
bool coalesceAndDiagnose(ArrayRef<ConstraintFix *> secondaryFixes,
|
||||
bool asNote = false) const override;
|
||||
|
||||
bool diagnose(bool asNote = false) const override;
|
||||
};
|
||||
|
||||
|
|
|
@ -3540,11 +3540,23 @@ bool ConstraintSystem::repairFailures(
|
|||
conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
|
||||
*this, lhs, rhs, getConstraintLocator(locator)));
|
||||
}
|
||||
if (lhs->is<TupleType>() && rhs->is<TupleType>()) {
|
||||
auto *fix = AllowTupleTypeMismatch::create(*this, lhs, rhs,
|
||||
getConstraintLocator(locator));
|
||||
conversionsOrFixes.push_back(fix);
|
||||
}
|
||||
|
||||
// Drop the `tuple element` locator element so that all tuple element
|
||||
// mismatches within the same tuple type can be coalesced later.
|
||||
path.pop_back();
|
||||
auto *tupleLocator = getConstraintLocator(locator.getAnchor(), path);
|
||||
|
||||
// TODO: Add a tailored fix for function parameter type mismatches.
|
||||
// Let this fail if it's a contextual mismatch with sequence element types,
|
||||
// as there's a special fix for that.
|
||||
if (tupleLocator->isLastElement<LocatorPathElt::FunctionArgument>() ||
|
||||
tupleLocator->isLastElement<LocatorPathElt::SequenceElementType>())
|
||||
break;
|
||||
|
||||
auto index = elt.getAs<LocatorPathElt::TupleElement>()->getIndex();
|
||||
auto *fix =
|
||||
AllowTupleTypeMismatch::create(*this, lhs, rhs, tupleLocator, index);
|
||||
conversionsOrFixes.push_back(fix);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -8476,6 +8488,12 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
|
|||
}
|
||||
|
||||
case FixKind::AllowTupleTypeMismatch: {
|
||||
if (fix->getAs<AllowTupleTypeMismatch>()->isElementMismatch()) {
|
||||
auto *locator = fix->getLocator();
|
||||
if (recordFix(fix, /*impact*/locator->isForContextualType() ? 5 : 1))
|
||||
return SolutionKind::Error;
|
||||
return SolutionKind::Solved;
|
||||
}
|
||||
auto lhs = type1->castTo<TupleType>();
|
||||
auto rhs = type2->castTo<TupleType>();
|
||||
// Create a new tuple type the size of the smaller tuple with elements
|
||||
|
@ -8487,6 +8505,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
|
|||
// 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<TupleTypeElt, 4> newTupleTypes;
|
||||
|
@ -8495,7 +8514,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
|
|||
auto largerElt = larger->getElement(i);
|
||||
if (i < smaller->getNumElements()) {
|
||||
auto smallerElt = smaller->getElement(i);
|
||||
if (largerElt.getType()->isTypeVariableOrMember() ||
|
||||
if (isLabelingFailure)
|
||||
newTupleTypes.push_back(TupleTypeElt(largerElt.getType()));
|
||||
else if (largerElt.getType()->isTypeVariableOrMember() ||
|
||||
smallerElt.getType()->isTypeVariableOrMember())
|
||||
newTupleTypes.push_back(largerElt);
|
||||
else
|
||||
|
|
|
@ -3410,6 +3410,164 @@ ConstraintSystem::getArgumentInfo(ConstraintLocator *locator) {
|
|||
return None;
|
||||
}
|
||||
|
||||
/// Given an apply expr, returns true if it is expected to have a direct callee
|
||||
/// overload, resolvable using `getChoiceFor`. Otherwise, returns false.
|
||||
static bool shouldHaveDirectCalleeOverload(const CallExpr *callExpr) {
|
||||
auto *fnExpr = callExpr->getDirectCallee();
|
||||
|
||||
// An apply of an apply/subscript doesn't have a direct callee.
|
||||
if (isa<ApplyExpr>(fnExpr) || isa<SubscriptExpr>(fnExpr))
|
||||
return false;
|
||||
|
||||
// Applies of closures don't have callee overloads.
|
||||
if (isa<ClosureExpr>(fnExpr))
|
||||
return false;
|
||||
|
||||
// No direct callee for a try!/try?.
|
||||
if (isa<ForceTryExpr>(fnExpr) || isa<OptionalTryExpr>(fnExpr))
|
||||
return false;
|
||||
|
||||
// If we have an intermediate cast, there's no direct callee.
|
||||
if (isa<ExplicitCastExpr>(fnExpr))
|
||||
return false;
|
||||
|
||||
// No direct callee for an if expr.
|
||||
if (isa<IfExpr>(fnExpr))
|
||||
return false;
|
||||
|
||||
// Assume that anything else would have a direct callee.
|
||||
return true;
|
||||
}
|
||||
|
||||
Type ConstraintSystem::resolveInterfaceType(Type type) const {
|
||||
auto resolvedType = type.transform([&](Type type) -> Type {
|
||||
if (auto *tvt = type->getAs<TypeVariableType>()) {
|
||||
// If this type variable is for a generic parameter, return that.
|
||||
if (auto *gp = tvt->getImpl().getGenericParameter())
|
||||
return gp;
|
||||
|
||||
// Otherwise resolve its fixed type, mapped out of context.
|
||||
if (auto fixed = getFixedType(tvt))
|
||||
return resolveInterfaceType(fixed->mapTypeOutOfContext());
|
||||
|
||||
return getRepresentative(tvt);
|
||||
}
|
||||
if (auto *dmt = type->getAs<DependentMemberType>()) {
|
||||
// For a dependent member, first resolve the base.
|
||||
auto newBase = resolveInterfaceType(dmt->getBase());
|
||||
|
||||
// Then reconstruct using its associated type.
|
||||
assert(dmt->getAssocType());
|
||||
return DependentMemberType::get(newBase, dmt->getAssocType());
|
||||
}
|
||||
return type;
|
||||
});
|
||||
|
||||
assert(!resolvedType->hasArchetype());
|
||||
return resolvedType;
|
||||
}
|
||||
|
||||
Optional<FunctionArgApplyInfo>
|
||||
ConstraintSystem::getFunctionArgApplyInfo(ConstraintLocator *locator) {
|
||||
auto *anchor = locator->getAnchor();
|
||||
auto path = locator->getPath();
|
||||
|
||||
// Look for the apply-arg-to-param element in the locator's path. We may
|
||||
// have to look through other elements that are generated from an argument
|
||||
// conversion such as GenericArgument for an optional-to-optional conversion,
|
||||
// and OptionalPayload for a value-to-optional conversion.
|
||||
auto iter = path.rbegin();
|
||||
auto applyArgElt = locator->findLast<LocatorPathElt::ApplyArgToParam>(iter);
|
||||
if (!applyArgElt)
|
||||
return None;
|
||||
|
||||
auto nextIter = iter + 1;
|
||||
assert(!locator->findLast<LocatorPathElt::ApplyArgToParam>(nextIter) &&
|
||||
"Multiple ApplyArgToParam components?");
|
||||
|
||||
// Form a new locator that ends at the apply-arg-to-param element, and
|
||||
// simplify it to get the full argument expression.
|
||||
auto argPath = path.drop_back(iter - path.rbegin());
|
||||
auto *argLocator = getConstraintLocator(
|
||||
anchor, argPath, ConstraintLocator::getSummaryFlagsForPath(argPath));
|
||||
|
||||
auto *argExpr = simplifyLocatorToAnchor(argLocator);
|
||||
|
||||
// If we were unable to simplify down to the argument expression, we don't
|
||||
// know what this is.
|
||||
if (!argExpr)
|
||||
return None;
|
||||
|
||||
Optional<OverloadChoice> choice;
|
||||
Type rawFnType;
|
||||
auto *calleeLocator = getCalleeLocator(argLocator);
|
||||
if (auto overload = findSelectedOverloadFor(calleeLocator)) {
|
||||
// If we have resolved an overload for the callee, then use that to get the
|
||||
// function type and callee.
|
||||
choice = overload->choice;
|
||||
rawFnType = overload->openedType;
|
||||
} else {
|
||||
// If we didn't resolve an overload for the callee, we should be dealing
|
||||
// with a call of an arbitrary function expr.
|
||||
if (auto *call = dyn_cast<CallExpr>(anchor)) {
|
||||
assert(!shouldHaveDirectCalleeOverload(call) &&
|
||||
"Should we have resolved a callee for this?");
|
||||
rawFnType = getType(call->getFn());
|
||||
} else {
|
||||
// FIXME: ArgumentMismatchFailure is currently used from CSDiag, meaning
|
||||
// we can end up a BinaryExpr here with an unresolved callee. It should be
|
||||
// possible to remove this once we've gotten rid of the old CSDiag logic
|
||||
// and just assert that we have a CallExpr.
|
||||
auto *apply = cast<ApplyExpr>(anchor);
|
||||
rawFnType = getType(apply->getFn());
|
||||
}
|
||||
}
|
||||
|
||||
// Try to resolve the function type by loading lvalues and looking through
|
||||
// optional types, which can occur for expressions like `fn?(5)`.
|
||||
auto *fnType = simplifyType(rawFnType)
|
||||
->getRValueType()
|
||||
->lookThroughAllOptionalTypes()
|
||||
->getAs<FunctionType>();
|
||||
if (!fnType)
|
||||
return None;
|
||||
|
||||
// Resolve the interface type for the function. Note that this may not be a
|
||||
// function type, for example it could be a generic parameter.
|
||||
Type fnInterfaceType;
|
||||
auto *callee = choice ? choice->getDeclOrNull() : nullptr;
|
||||
if (callee && callee->hasInterfaceType()) {
|
||||
// If we have a callee with an interface type, we can use it. This is
|
||||
// preferable to resolveInterfaceType, as this will allow us to get a
|
||||
// GenericFunctionType for generic decls.
|
||||
//
|
||||
// Note that it's possible to find a callee without an interface type. This
|
||||
// can happen for example with closure parameters, where the interface type
|
||||
// isn't set until the solution is applied. In that case, use
|
||||
// resolveInterfaceType.
|
||||
fnInterfaceType = callee->getInterfaceType();
|
||||
|
||||
// Strip off the curried self parameter if necessary.
|
||||
if (hasAppliedSelf(*this, *choice))
|
||||
fnInterfaceType = fnInterfaceType->castTo<AnyFunctionType>()->getResult();
|
||||
|
||||
if (auto *fn = fnInterfaceType->getAs<AnyFunctionType>()) {
|
||||
assert(fn->getNumParams() == fnType->getNumParams() &&
|
||||
"Parameter mismatch?");
|
||||
(void)fn;
|
||||
}
|
||||
} else {
|
||||
fnInterfaceType = resolveInterfaceType(rawFnType);
|
||||
}
|
||||
|
||||
auto argIdx = applyArgElt->getArgIdx();
|
||||
auto paramIdx = applyArgElt->getParamIdx();
|
||||
|
||||
return FunctionArgApplyInfo(getParentExpr(argExpr), argExpr, argIdx,
|
||||
simplifyType(getType(argExpr)),
|
||||
paramIdx, fnInterfaceType, fnType, callee);
|
||||
}
|
||||
|
||||
bool constraints::isKnownKeyPathType(Type type) {
|
||||
if (auto *BGT = type->getAs<BoundGenericType>())
|
||||
return isKnownKeyPathDecl(type->getASTContext(), BGT->getDecl());
|
||||
|
|
|
@ -486,6 +486,141 @@ struct SelectedOverload {
|
|||
Type boundType;
|
||||
};
|
||||
|
||||
/// Provides information about the application of a function argument to a
|
||||
/// parameter.
|
||||
class FunctionArgApplyInfo {
|
||||
Expr *ArgListExpr;
|
||||
Expr *ArgExpr;
|
||||
unsigned ArgIdx;
|
||||
Type ArgType;
|
||||
|
||||
unsigned ParamIdx;
|
||||
|
||||
Type FnInterfaceType;
|
||||
FunctionType *FnType;
|
||||
const ValueDecl *Callee;
|
||||
|
||||
public:
|
||||
FunctionArgApplyInfo(Expr *argListExpr, Expr *argExpr, unsigned argIdx,
|
||||
Type argType, unsigned paramIdx, Type fnInterfaceType,
|
||||
FunctionType *fnType, const ValueDecl *callee)
|
||||
: ArgListExpr(argListExpr), ArgExpr(argExpr), ArgIdx(argIdx),
|
||||
ArgType(argType), ParamIdx(paramIdx), FnInterfaceType(fnInterfaceType),
|
||||
FnType(fnType), Callee(callee) {}
|
||||
|
||||
/// \returns The argument being applied.
|
||||
Expr *getArgExpr() const { return ArgExpr; }
|
||||
|
||||
/// \returns The position of the argument, starting at 1.
|
||||
unsigned getArgPosition() const { return ArgIdx + 1; }
|
||||
|
||||
/// \returns The position of the parameter, starting at 1.
|
||||
unsigned getParamPosition() const { return ParamIdx + 1; }
|
||||
|
||||
/// \returns The type of the argument being applied, including any generic
|
||||
/// substitutions.
|
||||
///
|
||||
/// \param withSpecifier Whether to keep the inout or @lvalue specifier of
|
||||
/// the argument, if any.
|
||||
Type getArgType(bool withSpecifier = false) const {
|
||||
return withSpecifier ? ArgType : ArgType->getWithoutSpecifierType();
|
||||
}
|
||||
|
||||
/// \returns The label for the argument being applied.
|
||||
Identifier getArgLabel() const {
|
||||
if (auto *te = dyn_cast<TupleExpr>(ArgListExpr))
|
||||
return te->getElementName(ArgIdx);
|
||||
|
||||
assert(isa<ParenExpr>(ArgListExpr));
|
||||
return Identifier();
|
||||
}
|
||||
|
||||
/// \returns A textual description of the argument suitable for diagnostics.
|
||||
/// For an argument with an unambiguous label, this will the label. Otherwise
|
||||
/// it will be its position in the argument list.
|
||||
StringRef getArgDescription(SmallVectorImpl<char> &scratch) const {
|
||||
llvm::raw_svector_ostream stream(scratch);
|
||||
|
||||
// Use the argument label only if it's unique within the argument list.
|
||||
auto argLabel = getArgLabel();
|
||||
auto useArgLabel = [&]() -> bool {
|
||||
if (argLabel.empty())
|
||||
return false;
|
||||
|
||||
if (auto *te = dyn_cast<TupleExpr>(ArgListExpr))
|
||||
return llvm::count(te->getElementNames(), argLabel) == 1;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (useArgLabel()) {
|
||||
stream << "'";
|
||||
stream << argLabel;
|
||||
stream << "'";
|
||||
} else {
|
||||
stream << "#";
|
||||
stream << getArgPosition();
|
||||
}
|
||||
return StringRef(scratch.data(), scratch.size());
|
||||
}
|
||||
|
||||
/// \returns The interface type for the function being applied. Note that this
|
||||
/// may not a function type, for example it could be a generic parameter.
|
||||
Type getFnInterfaceType() const { return FnInterfaceType; }
|
||||
|
||||
/// \returns The function type being applied, including any generic
|
||||
/// substitutions.
|
||||
FunctionType *getFnType() const { return FnType; }
|
||||
|
||||
/// \returns The callee for the application.
|
||||
const ValueDecl *getCallee() const { return Callee; }
|
||||
|
||||
private:
|
||||
Type getParamTypeImpl(AnyFunctionType *fnTy,
|
||||
bool lookThroughAutoclosure) const {
|
||||
auto param = fnTy->getParams()[ParamIdx];
|
||||
auto paramTy = param.getPlainType();
|
||||
if (lookThroughAutoclosure && param.isAutoClosure())
|
||||
paramTy = paramTy->castTo<FunctionType>()->getResult();
|
||||
return paramTy;
|
||||
}
|
||||
|
||||
public:
|
||||
/// \returns The type of the parameter which the argument is being applied to,
|
||||
/// including any generic substitutions.
|
||||
///
|
||||
/// \param lookThroughAutoclosure Whether an @autoclosure () -> T parameter
|
||||
/// should be treated as being of type T.
|
||||
Type getParamType(bool lookThroughAutoclosure = true) const {
|
||||
return getParamTypeImpl(FnType, lookThroughAutoclosure);
|
||||
}
|
||||
|
||||
/// \returns The interface type of the parameter which the argument is being
|
||||
/// applied to.
|
||||
///
|
||||
/// \param lookThroughAutoclosure Whether an @autoclosure () -> T parameter
|
||||
/// should be treated as being of type T.
|
||||
Type getParamInterfaceType(bool lookThroughAutoclosure = true) const {
|
||||
auto interfaceFnTy = FnInterfaceType->getAs<AnyFunctionType>();
|
||||
if (!interfaceFnTy) {
|
||||
// If the interface type isn't a function, then just return the resolved
|
||||
// parameter type.
|
||||
return getParamType(lookThroughAutoclosure)->mapTypeOutOfContext();
|
||||
}
|
||||
return getParamTypeImpl(interfaceFnTy, lookThroughAutoclosure);
|
||||
}
|
||||
|
||||
/// \returns The flags of the parameter which the argument is being applied
|
||||
/// to.
|
||||
ParameterTypeFlags getParameterFlags() const {
|
||||
return FnType->getParams()[ParamIdx].getParameterFlags();
|
||||
}
|
||||
|
||||
ParameterTypeFlags getParameterFlagsAtIndex(unsigned idx) const {
|
||||
return FnType->getParams()[idx].getParameterFlags();
|
||||
}
|
||||
};
|
||||
|
||||
/// Describes an aspect of a solution that affects its overall score, i.e., a
|
||||
/// user-defined conversions.
|
||||
enum ScoreKind {
|
||||
|
@ -1597,6 +1732,16 @@ public:
|
|||
return findSelectedOverloadFor(calleeLoc);
|
||||
}
|
||||
|
||||
/// Resolve type variables present in the raw type, using generic parameter
|
||||
/// types where possible.
|
||||
Type resolveInterfaceType(Type type) const;
|
||||
|
||||
/// For a given locator describing a function argument conversion, or a
|
||||
/// constraint within an argument conversion, returns information about the
|
||||
/// application of the argument to its parameter. If the locator is not
|
||||
/// for an argument conversion, returns \c None.
|
||||
Optional<FunctionArgApplyInfo> getFunctionArgApplyInfo(ConstraintLocator *);
|
||||
|
||||
private:
|
||||
unsigned assignTypeVariableID() {
|
||||
return TypeCounter++;
|
||||
|
|
|
@ -512,7 +512,7 @@ let _: Color = .Unknown(42) // expected-error {{missing argument label 'descript
|
|||
// expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'String'}}
|
||||
let _ : Color = .rainbow(42) // expected-error {{argument passed to call that takes no arguments}}
|
||||
|
||||
let _ : (Int, Float) = (42.0, 12) // expected-error {{cannot convert value of type 'Double' to specified type 'Int'}}
|
||||
let _ : (Int, Float) = (42.0, 12) // expected-error {{cannot convert value of type '(Double, Float)' to specified type '(Int, Float)'}}
|
||||
|
||||
let _ : Color = .rainbow // expected-error {{member 'rainbow()' is a function; did you mean to call it?}} {{25-25=()}}
|
||||
|
||||
|
|
|
@ -19,5 +19,5 @@ struct S: P {
|
|||
typealias R = T3
|
||||
|
||||
static let foo: (T1, (R) -> T2) = bind()
|
||||
// expected-error@-1 {{tuple type '(T1, (S.R) -> T3)' (aka '(Int, (Bool) -> Bool)') is not convertible to tuple '(T1, (S.R) -> T2)' (aka '(Int, (Bool) -> Float)')}}
|
||||
// expected-error@-1 {{cannot convert value of type '(T1, (S.R) -> T3)' (aka '(Int, (Bool) -> Bool)') to specified type '(T1, (S.R) -> T2)' (aka '(Int, (Bool) -> Float)')}}
|
||||
}
|
||||
|
|
|
@ -457,6 +457,7 @@ func assignmentsToFuncs() {
|
|||
|
||||
var x = 0
|
||||
(x, func1() = 0) = (4, 5) // expected-error {{expression is not assignable: 'func1' returns immutable value}}
|
||||
// expected-error@-1 {{cannot assign value of type '(Int, Int)' to type '(Int, ())'}}
|
||||
}
|
||||
|
||||
// <rdar://problem/17051675> Structure initializers in an extension cannot assign to constant properties
|
||||
|
|
|
@ -45,7 +45,7 @@ typealias Tuple3<T1> = (T1, T1) where T1 : Hashable
|
|||
|
||||
let _ : Tuple2<Int, String> = (1, "foo")
|
||||
let _ : Tuple2 = (1, "foo")
|
||||
let _ : Tuple2<Int, String> = ("bar", // expected-error {{cannot convert value of type 'String' to specified type 'Int'}}
|
||||
let _ : Tuple2<Int, String> = ("bar", // expected-error {{cannot convert value of type '(String, String)' to specified type 'Tuple2<Int, String>' (aka '(Int, String)')}}
|
||||
"foo")
|
||||
|
||||
func f() {
|
||||
|
|
|
@ -96,7 +96,7 @@ func tuplePatternDestructuring(_ x : Int, y : Int) {
|
|||
_ = i+j
|
||||
|
||||
// <rdar://problem/20395243> QoI: type variable reconstruction failing for tuple types
|
||||
let (x: g1, a: h1) = (b: x, a: y) // expected-error {{tuple type '(b: Int, a: Int)' is not convertible to tuple '(x: Int, a: Int)'}}
|
||||
let (x: g1, a: h1) = (b: x, a: y) // expected-error {{tuple type '(b: Int, a: Int)' is not convertible to tuple type '(x: Int, a: Int)'}}
|
||||
}
|
||||
|
||||
// <rdar://problem/21057425> Crash while compiling attached test-app.
|
||||
|
|
|
@ -87,8 +87,8 @@ Double(1) as Double as String // expected-error{{cannot convert value of type 'D
|
|||
// expected-error@-1 2 {{cannot convert value of type 'Int' to expected element type 'String'}}
|
||||
// expected-error@-2 {{cannot convert value of type 'Double' to expected element type 'String'}}
|
||||
[[1]] as [[String]] // expected-error{{cannot convert value of type 'Int' to expected element type 'String'}}
|
||||
(1, 1.0) as (Int, Int) // expected-error{{cannot convert value of type 'Double' to type 'Int' in coercion}}
|
||||
(1.0, 1, "asd") as (String, Int, Float) // expected-error{{cannot convert value of type 'Double' to type 'String' in coercion}}
|
||||
(1, 1.0) as (Int, Int) // expected-error{{cannot convert value of type '(Int, Double)' to type '(Int, Int)' in coercion}}
|
||||
(1.0, 1, "asd") as (String, Int, Float) // expected-error{{cannot convert value of type '(Double, Int, String)' to type '(String, Int, Float)' in coercion}}
|
||||
(1, 1.0, "a", [1, 23]) as (Int, Double, String, [String])
|
||||
// expected-error@-1 2 {{cannot convert value of type 'Int' to expected element type 'String'}}
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ func errorRecovery() {
|
|||
// <rdar://problem/22426860> CrashTracer: [USER] swift at …mous_namespace::ConstraintGenerator::getTypeForPattern + 698
|
||||
var (g1, g2, g3) = (1, 2) // expected-error {{'(Int, Int)' is not convertible to '(Int, Int, _)', tuples have a different number of elements}}
|
||||
var (h1, h2) = (1, 2, 3) // expected-error {{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
|
||||
var i: (Bool, Bool) = makeTuple() // expected-error {{tuple type '(String, Int)' is not convertible to tuple '(Bool, Bool)'}}
|
||||
var i: (Bool, Bool) = makeTuple() // expected-error {{cannot convert value of type '(String, Int)' to specified type '(Bool, Bool)'}}
|
||||
}
|
||||
|
||||
func acceptsInt(_ x: Int) {}
|
||||
|
@ -189,7 +189,7 @@ func test4() -> ((_ arg1: Int, _ arg2: Int) -> Int) {
|
|||
func test5() {
|
||||
let a: (Int, Int) = (1,2)
|
||||
var
|
||||
_: ((Int) -> Int, Int) = a // expected-error {{tuple type '(Int, Int)' is not convertible to tuple '((Int) -> Int, Int)'}}
|
||||
_: ((Int) -> Int, Int) = a // expected-error {{cannot convert value of type '(Int, Int)' to specified type '((Int) -> Int, Int)'}}
|
||||
|
||||
|
||||
let c: (a: Int, b: Int) = (1,2)
|
||||
|
|
Загрузка…
Ссылка в новой задаче