| //===--- CSFix.cpp - Constraint Fixes -------------------------------------===// |
| // |
| // 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 the \c ConstraintFix class and its related types, |
| // which is used by constraint solver to attempt to fix constraints to be |
| // able to produce a solution which is easily diagnosable. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "CSDiagnostics.h" |
| #include "swift/AST/Expr.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/AST/Type.h" |
| #include "swift/AST/Types.h" |
| #include "swift/Basic/SourceManager.h" |
| #include "swift/Sema/ConstraintLocator.h" |
| #include "swift/Sema/ConstraintSystem.h" |
| #include "swift/Sema/CSFix.h" |
| #include "swift/Sema/OverloadChoice.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <string> |
| |
| using namespace swift; |
| using namespace constraints; |
| |
| ConstraintFix::~ConstraintFix() {} |
| |
| ASTNode ConstraintFix::getAnchor() const { return getLocator()->getAnchor(); } |
| |
| void ConstraintFix::print(llvm::raw_ostream &Out) const { |
| Out << "[fix: "; |
| Out << getName(); |
| Out << ']'; |
| Out << " @ "; |
| getLocator()->dump(&CS.getASTContext().SourceMgr, Out); |
| } |
| |
| void ConstraintFix::dump() const {print(llvm::errs()); } |
| |
| std::string ForceDowncast::getName() const { |
| llvm::SmallString<16> name; |
| name += "force downcast ("; |
| name += getFromType()->getString(); |
| name += " as! "; |
| name += getToType()->getString(); |
| name += ")"; |
| return name.c_str(); |
| } |
| |
| bool ForceDowncast::diagnose(const Solution &solution, bool asNote) const { |
| MissingExplicitConversionFailure failure(solution, getFromType(), getToType(), |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| ForceDowncast *ForceDowncast::create(ConstraintSystem &cs, Type fromType, |
| Type toType, ConstraintLocator *locator) { |
| return new (cs.getAllocator()) ForceDowncast(cs, fromType, toType, locator); |
| } |
| |
| bool ForceOptional::diagnose(const Solution &solution, bool asNote) const { |
| MissingOptionalUnwrapFailure failure(solution, getFromType(), getToType(), |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| ForceOptional *ForceOptional::create(ConstraintSystem &cs, Type fromType, |
| Type toType, ConstraintLocator *locator) { |
| return new (cs.getAllocator()) ForceOptional(cs, fromType, toType, locator); |
| } |
| |
| bool UnwrapOptionalBase::diagnose(const Solution &solution, bool asNote) const { |
| bool resultIsOptional = |
| getKind() == FixKind::UnwrapOptionalBaseWithOptionalResult; |
| MemberAccessOnOptionalBaseFailure failure(solution, getLocator(), MemberName, |
| MemberBaseType, resultIsOptional); |
| return failure.diagnose(asNote); |
| } |
| |
| UnwrapOptionalBase *UnwrapOptionalBase::create(ConstraintSystem &cs, |
| DeclNameRef member, |
| Type memberBaseType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) UnwrapOptionalBase( |
| cs, FixKind::UnwrapOptionalBase, member, memberBaseType, locator); |
| } |
| |
| UnwrapOptionalBase *UnwrapOptionalBase::createWithOptionalResult( |
| ConstraintSystem &cs, DeclNameRef member, Type memberBaseType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| UnwrapOptionalBase(cs, FixKind::UnwrapOptionalBaseWithOptionalResult, |
| member, memberBaseType, locator); |
| } |
| |
| bool AddAddressOf::diagnose(const Solution &solution, bool asNote) const { |
| MissingAddressOfFailure failure(solution, getFromType(), getToType(), |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AddAddressOf *AddAddressOf::create(ConstraintSystem &cs, Type argTy, |
| Type paramTy, ConstraintLocator *locator) { |
| return new (cs.getAllocator()) AddAddressOf(cs, argTy, paramTy, locator); |
| } |
| |
| bool TreatRValueAsLValue::diagnose(const Solution &solution, |
| bool asNote) const { |
| RValueTreatedAsLValueFailure failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| TreatRValueAsLValue *TreatRValueAsLValue::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| if (locator->isLastElement<LocatorPathElt::ApplyArgToParam>()) |
| locator = cs.getConstraintLocator( |
| locator, LocatorPathElt::ArgumentAttribute::forInOut()); |
| |
| return new (cs.getAllocator()) TreatRValueAsLValue(cs, locator); |
| } |
| |
| bool CoerceToCheckedCast::diagnose(const Solution &solution, |
| bool asNote) const { |
| InvalidCoercionFailure failure(solution, getFromType(), getToType(), |
| UseConditionalCast, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| CoerceToCheckedCast *CoerceToCheckedCast::attempt(ConstraintSystem &cs, |
| Type fromType, Type toType, |
| bool useConditionalCast, |
| ConstraintLocator *locator) { |
| // If any of the types has a type variable, don't add the fix. |
| if (fromType->hasTypeVariable() || toType->hasTypeVariable()) |
| return nullptr; |
| |
| auto anchor = locator->getAnchor(); |
| if (auto *assignExpr = getAsExpr<AssignExpr>(anchor)) |
| anchor = assignExpr->getSrc(); |
| auto *coerceExpr = getAsExpr<CoerceExpr>(anchor); |
| if (!coerceExpr) |
| return nullptr; |
| |
| const auto castKind = TypeChecker::typeCheckCheckedCast( |
| fromType, toType, CheckedCastContextKind::Coercion, cs.DC, |
| SourceLoc(), coerceExpr->getSubExpr(), SourceRange()); |
| |
| // Invalid cast. |
| if (castKind == CheckedCastKind::Unresolved) |
| return nullptr; |
| |
| return new (cs.getAllocator()) |
| CoerceToCheckedCast(cs, fromType, toType, useConditionalCast, locator); |
| } |
| |
| bool TreatArrayLiteralAsDictionary::diagnose(const Solution &solution, |
| bool asNote) const { |
| ArrayLiteralToDictionaryConversionFailure failure(solution, |
| getToType(), getFromType(), |
| getLocator()); |
| return failure.diagnose(asNote); |
| }; |
| |
| TreatArrayLiteralAsDictionary * |
| TreatArrayLiteralAsDictionary::create(ConstraintSystem &cs, |
| Type dictionaryTy, Type arrayTy, |
| ConstraintLocator *locator) { |
| assert(getAsExpr<ArrayExpr>(locator->getAnchor())->getNumElements() <= 1); |
| return new (cs.getAllocator()) |
| TreatArrayLiteralAsDictionary(cs, dictionaryTy, arrayTy, locator); |
| }; |
| |
| bool MarkExplicitlyEscaping::diagnose(const Solution &solution, |
| bool asNote) const { |
| NoEscapeFuncToTypeConversionFailure failure(solution, getFromType(), |
| getToType(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| MarkExplicitlyEscaping * |
| MarkExplicitlyEscaping::create(ConstraintSystem &cs, Type lhs, Type rhs, |
| ConstraintLocator *locator) { |
| if (locator->isLastElement<LocatorPathElt::ApplyArgToParam>()) |
| locator = cs.getConstraintLocator( |
| locator, LocatorPathElt::ArgumentAttribute::forEscaping()); |
| |
| return new (cs.getAllocator()) MarkExplicitlyEscaping(cs, lhs, rhs, locator); |
| } |
| |
| bool RelabelArguments::diagnose(const Solution &solution, bool asNote) const { |
| LabelingFailure failure(solution, getLocator(), getLabels()); |
| return failure.diagnose(asNote); |
| } |
| |
| RelabelArguments * |
| RelabelArguments::create(ConstraintSystem &cs, |
| llvm::ArrayRef<Identifier> correctLabels, |
| ConstraintLocator *locator) { |
| unsigned size = totalSizeToAlloc<Identifier>(correctLabels.size()); |
| void *mem = cs.getAllocator().Allocate(size, alignof(RelabelArguments)); |
| return new (mem) RelabelArguments(cs, correctLabels, locator); |
| } |
| |
| bool MissingConformance::diagnose(const Solution &solution, bool asNote) const { |
| auto *locator = getLocator(); |
| |
| if (IsContextual) { |
| auto &cs = solution.getConstraintSystem(); |
| auto context = cs.getContextualTypePurpose(locator->getAnchor()); |
| MissingContextualConformanceFailure failure( |
| solution, context, NonConformingType, ProtocolType, locator); |
| return failure.diagnose(asNote); |
| } |
| |
| MissingConformanceFailure failure( |
| solution, locator, std::make_pair(NonConformingType, ProtocolType)); |
| return failure.diagnose(asNote); |
| } |
| |
| MissingConformance * |
| MissingConformance::forContextual(ConstraintSystem &cs, Type type, |
| Type protocolType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) MissingConformance( |
| cs, /*isContextual=*/true, type, protocolType, locator); |
| } |
| |
| MissingConformance * |
| MissingConformance::forRequirement(ConstraintSystem &cs, Type type, |
| Type protocolType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) MissingConformance( |
| cs, /*isContextual=*/false, type, protocolType, locator); |
| } |
| |
| bool SkipSameTypeRequirement::diagnose(const Solution &solution, |
| bool asNote) const { |
| SameTypeRequirementFailure failure(solution, LHS, RHS, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| SkipSameTypeRequirement * |
| SkipSameTypeRequirement::create(ConstraintSystem &cs, Type lhs, Type rhs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) SkipSameTypeRequirement(cs, lhs, rhs, locator); |
| } |
| |
| bool SkipSuperclassRequirement::diagnose(const Solution &solution, |
| bool asNote) const { |
| SuperclassRequirementFailure failure(solution, LHS, RHS, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| SkipSuperclassRequirement * |
| SkipSuperclassRequirement::create(ConstraintSystem &cs, Type lhs, Type rhs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| SkipSuperclassRequirement(cs, lhs, rhs, locator); |
| } |
| |
| bool ContextualMismatch::diagnose(const Solution &solution, bool asNote) const { |
| ContextualFailure failure(solution, getFromType(), getToType(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| bool ContextualMismatch::diagnoseForAmbiguity( |
| CommonFixesArray commonFixes) const { |
| auto getTypes = |
| [&](const std::pair<const Solution *, const ConstraintFix *> &entry) |
| -> std::pair<Type, Type> { |
| auto &solution = *entry.first; |
| auto *fix = static_cast<const ContextualMismatch *>(entry.second); |
| |
| return {solution.simplifyType(fix->getFromType()), |
| solution.simplifyType(fix->getToType())}; |
| }; |
| |
| auto etalonTypes = getTypes(commonFixes.front()); |
| if (llvm::all_of( |
| commonFixes, |
| [&](const std::pair<const Solution *, const ConstraintFix *> &entry) { |
| auto types = getTypes(entry); |
| return etalonTypes.first->isEqual(types.first) && |
| etalonTypes.second->isEqual(types.second); |
| })) { |
| const auto &primary = commonFixes.front(); |
| return primary.second->diagnose(*primary.first, /*asNote=*/false); |
| } |
| |
| return false; |
| } |
| |
| ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs, |
| Type rhs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) ContextualMismatch(cs, lhs, rhs, locator); |
| } |
| |
| /// Computes the contextual type information for a type mismatch of a |
| /// component in a structural type (tuple or function type). |
| /// |
| /// \returns A tuple containing the contextual type purpose, the source type, |
| /// and the contextual type. |
| static Optional<std::tuple<ContextualTypePurpose, Type, Type>> |
| getStructuralTypeContext(const Solution &solution, ConstraintLocator *locator) { |
| if (locator->findLast<LocatorPathElt::ContextualType>()) { |
| assert(locator->isLastElement<LocatorPathElt::ContextualType>() || |
| locator->isLastElement<LocatorPathElt::FunctionArgument>()); |
| |
| auto &cs = solution.getConstraintSystem(); |
| auto anchor = locator->getAnchor(); |
| auto contextualType = cs.getContextualType(anchor); |
| auto exprType = cs.getType(anchor); |
| return std::make_tuple(cs.getContextualTypePurpose(anchor), exprType, |
| contextualType); |
| } else if (auto argApplyInfo = solution.getFunctionArgApplyInfo(locator)) { |
| return std::make_tuple(CTP_CallArgument, |
| argApplyInfo->getArgType(), |
| argApplyInfo->getParamType()); |
| } else if (auto *coerceExpr = getAsExpr<CoerceExpr>(locator->getAnchor())) { |
| return std::make_tuple(CTP_CoerceOperand, |
| solution.getType(coerceExpr->getSubExpr()), |
| solution.getType(coerceExpr)); |
| } else if (auto *assignExpr = getAsExpr<AssignExpr>(locator->getAnchor())) { |
| auto CTP = isa<SubscriptExpr>(assignExpr->getDest()) ? CTP_SubscriptAssignSource |
| : CTP_AssignSource; |
| return std::make_tuple(CTP, |
| solution.getType(assignExpr->getSrc()), |
| solution.getType(assignExpr->getDest())->getRValueType()); |
| } else if (auto *call = getAsExpr<CallExpr>(locator->getAnchor())) { |
| assert(isa<TypeExpr>(call->getFn())); |
| return std::make_tuple( |
| CTP_Initialization, |
| solution.getType(call->getFn())->getMetatypeInstanceType(), |
| solution.getType(call->getArg())); |
| } |
| |
| return None; |
| } |
| |
| bool AllowTupleTypeMismatch::coalesceAndDiagnose( |
| const Solution &solution, 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(); |
| ContextualTypePurpose purpose; |
| Type fromType; |
| Type toType; |
| |
| if (getFromType()->is<TupleType>() && getToType()->is<TupleType>()) { |
| purpose = cs.getContextualTypePurpose(locator->getAnchor()); |
| fromType = getFromType(); |
| toType = getToType(); |
| } else if (auto contextualTypeInfo = |
| getStructuralTypeContext(solution, locator)) { |
| std::tie(purpose, fromType, toType) = *contextualTypeInfo; |
| } else { |
| return false; |
| } |
| |
| TupleContextualFailure failure(solution, purpose, |
| fromType->lookThroughAllOptionalTypes(), |
| toType->lookThroughAllOptionalTypes(), |
| indices, locator); |
| return failure.diagnose(asNote); |
| } |
| |
| bool AllowTupleTypeMismatch::diagnose(const Solution &solution, |
| bool asNote) const { |
| return coalesceAndDiagnose(solution, {}, asNote); |
| } |
| |
| AllowTupleTypeMismatch * |
| AllowTupleTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs, |
| ConstraintLocator *locator, |
| Optional<unsigned> index) { |
| return new (cs.getAllocator()) |
| AllowTupleTypeMismatch(cs, lhs, rhs, locator, index); |
| } |
| |
| bool AllowFunctionTypeMismatch::coalesceAndDiagnose( |
| const Solution &solution, ArrayRef<ConstraintFix *> fixes, |
| bool asNote) const { |
| llvm::SmallVector<unsigned, 4> indices{ParamIndex}; |
| |
| for (auto fix : fixes) { |
| if (auto *fnFix = fix->getAs<AllowFunctionTypeMismatch>()) |
| indices.push_back(fnFix->ParamIndex); |
| } |
| |
| auto *locator = getLocator(); |
| ContextualTypePurpose purpose; |
| Type fromType; |
| Type toType; |
| |
| auto contextualTypeInfo = getStructuralTypeContext(solution, locator); |
| if (!contextualTypeInfo) |
| return false; |
| |
| std::tie(purpose, fromType, toType) = *contextualTypeInfo; |
| FunctionTypeMismatch failure(solution, purpose, fromType, toType, indices, |
| locator); |
| return failure.diagnose(asNote); |
| } |
| |
| bool AllowFunctionTypeMismatch::diagnose(const Solution &solution, |
| bool asNote) const { |
| return coalesceAndDiagnose(solution, {}, asNote); |
| } |
| |
| bool AllowFunctionTypeMismatch::diagnoseForAmbiguity( |
| CommonFixesArray commonFixes) const { |
| if (ContextualMismatch::diagnoseForAmbiguity(commonFixes)) |
| return true; |
| |
| auto *locator = getLocator(); |
| // If this is a mismatch between two function types at argument |
| // position, there is a tailored diagnostic for that. |
| if (auto argConv = |
| locator->getLastElementAs<LocatorPathElt::ApplyArgToParam>()) { |
| auto &cs = getConstraintSystem(); |
| auto &DE = cs.getASTContext().Diags; |
| auto &solution = *commonFixes[0].first; |
| |
| auto info = getStructuralTypeContext(solution, locator); |
| if (!info) |
| return false; |
| |
| auto *argLoc = cs.getConstraintLocator(simplifyLocatorToAnchor(locator)); |
| |
| auto overload = solution.getOverloadChoiceIfAvailable( |
| solution.getCalleeLocator(argLoc)); |
| if (!overload) |
| return false; |
| |
| auto name = overload->choice.getName().getBaseName(); |
| DE.diagnose(getLoc(getAnchor()), diag::no_candidates_match_argument_type, |
| name.userFacingName(), std::get<2>(*info), |
| argConv->getParamIdx()); |
| |
| for (auto &entry : commonFixes) { |
| auto &solution = *entry.first; |
| auto overload = solution.getOverloadChoiceIfAvailable( |
| solution.getCalleeLocator(argLoc)); |
| |
| if (!(overload && overload->choice.isDecl())) |
| continue; |
| |
| auto *decl = overload->choice.getDecl(); |
| if (decl->getLoc().isValid()) { |
| DE.diagnose(decl, diag::found_candidate_type, |
| solution.simplifyType(overload->openedType)); |
| } |
| } |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| AllowFunctionTypeMismatch * |
| AllowFunctionTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs, |
| ConstraintLocator *locator, unsigned index) { |
| return new (cs.getAllocator()) |
| AllowFunctionTypeMismatch(cs, lhs, rhs, locator, index); |
| } |
| |
| bool GenericArgumentsMismatch::diagnose(const Solution &solution, |
| bool asNote) const { |
| GenericArgumentsMismatchFailure failure(solution, getFromType(), getToType(), |
| getMismatches(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| GenericArgumentsMismatch *GenericArgumentsMismatch::create( |
| ConstraintSystem &cs, Type actual, Type required, |
| llvm::ArrayRef<unsigned> mismatches, ConstraintLocator *locator) { |
| unsigned size = totalSizeToAlloc<unsigned>(mismatches.size()); |
| void *mem = |
| cs.getAllocator().Allocate(size, alignof(GenericArgumentsMismatch)); |
| return new (mem) |
| GenericArgumentsMismatch(cs, actual, required, mismatches, locator); |
| } |
| |
| bool AutoClosureForwarding::diagnose(const Solution &solution, |
| bool asNote) const { |
| AutoClosureForwardingFailure failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AutoClosureForwarding *AutoClosureForwarding::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) AutoClosureForwarding(cs, locator); |
| } |
| |
| bool AllowAutoClosurePointerConversion::diagnose(const Solution &solution, |
| bool asNote) const { |
| AutoClosurePointerConversionFailure failure(solution, getFromType(), |
| getToType(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowAutoClosurePointerConversion * |
| AllowAutoClosurePointerConversion::create(ConstraintSystem &cs, Type pointeeType, |
| Type pointerType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowAutoClosurePointerConversion(cs, pointeeType, pointerType, locator); |
| } |
| |
| bool RemoveUnwrap::diagnose(const Solution &solution, bool asNote) const { |
| NonOptionalUnwrapFailure failure(solution, BaseType, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| RemoveUnwrap *RemoveUnwrap::create(ConstraintSystem &cs, Type baseType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) RemoveUnwrap(cs, baseType, locator); |
| } |
| |
| bool InsertExplicitCall::diagnose(const Solution &solution, bool asNote) const { |
| MissingCallFailure failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| InsertExplicitCall *InsertExplicitCall::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) InsertExplicitCall(cs, locator); |
| } |
| |
| bool UsePropertyWrapper::diagnose(const Solution &solution, bool asNote) const { |
| ExtraneousPropertyWrapperUnwrapFailure failure( |
| solution, Wrapped, UsingProjection, Base, Wrapper, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| UsePropertyWrapper *UsePropertyWrapper::create(ConstraintSystem &cs, |
| VarDecl *wrapped, |
| bool usingProjection, |
| Type base, Type wrapper, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) UsePropertyWrapper( |
| cs, wrapped, usingProjection, base, wrapper, locator); |
| } |
| |
| bool UseWrappedValue::diagnose(const Solution &solution, bool asNote) const { |
| MissingPropertyWrapperUnwrapFailure failure(solution, PropertyWrapper, |
| usingProjection(), Base, |
| Wrapper, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| UseWrappedValue *UseWrappedValue::create(ConstraintSystem &cs, |
| VarDecl *propertyWrapper, Type base, |
| Type wrapper, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| UseWrappedValue(cs, propertyWrapper, base, wrapper, locator); |
| } |
| |
| bool UseSubscriptOperator::diagnose(const Solution &solution, |
| bool asNote) const { |
| SubscriptMisuseFailure failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| UseSubscriptOperator *UseSubscriptOperator::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) UseSubscriptOperator(cs, locator); |
| } |
| |
| bool DefineMemberBasedOnUse::diagnose(const Solution &solution, |
| bool asNote) const { |
| MissingMemberFailure failure(solution, BaseType, Name, getLocator()); |
| return AlreadyDiagnosed || failure.diagnose(asNote); |
| } |
| |
| bool |
| DefineMemberBasedOnUse::diagnoseForAmbiguity(CommonFixesArray commonFixes) const { |
| Type concreteBaseType; |
| for (const auto &solutionAndFix : commonFixes) { |
| const auto *solution = solutionAndFix.first; |
| const auto *fix = solutionAndFix.second->getAs<DefineMemberBasedOnUse>(); |
| |
| auto baseType = solution->simplifyType(fix->BaseType); |
| if (!concreteBaseType) |
| concreteBaseType = baseType; |
| |
| if (concreteBaseType->getCanonicalType() != baseType->getCanonicalType()) { |
| auto &DE = getConstraintSystem().getASTContext().Diags; |
| DE.diagnose(getLoc(getAnchor()), diag::unresolved_member_no_inference, |
| Name); |
| return true; |
| } |
| } |
| |
| return diagnose(*commonFixes.front().first); |
| } |
| |
| DefineMemberBasedOnUse * |
| DefineMemberBasedOnUse::create(ConstraintSystem &cs, Type baseType, |
| DeclNameRef member, bool alreadyDiagnosed, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| DefineMemberBasedOnUse(cs, baseType, member, alreadyDiagnosed, locator); |
| } |
| |
| bool DefineMemberBasedOnUnintendedGenericParam::diagnose( |
| const Solution &solution, bool asNote) const { |
| UnintendedExtraGenericParamMemberFailure failure(solution, BaseType, Name, |
| ParamName, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| DefineMemberBasedOnUnintendedGenericParam * |
| DefineMemberBasedOnUnintendedGenericParam::create(ConstraintSystem &cs, |
| Type baseType, |
| DeclNameRef member, |
| Identifier paramName, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) DefineMemberBasedOnUnintendedGenericParam( |
| cs, baseType, member, paramName, locator); |
| } |
| |
| AllowMemberRefOnExistential * |
| AllowMemberRefOnExistential::create(ConstraintSystem &cs, Type baseType, |
| ValueDecl *member, DeclNameRef memberName, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowMemberRefOnExistential(cs, baseType, memberName, member, locator); |
| } |
| |
| bool AllowMemberRefOnExistential::diagnose(const Solution &solution, |
| bool asNote) const { |
| InvalidMemberRefOnExistential failure(solution, getBaseType(), |
| getMemberName(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| bool AllowTypeOrInstanceMember::diagnose(const Solution &solution, |
| bool asNote) const { |
| AllowTypeOrInstanceMemberFailure failure(solution, getBaseType(), getMember(), |
| getMemberName(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowTypeOrInstanceMember * |
| AllowTypeOrInstanceMember::create(ConstraintSystem &cs, Type baseType, |
| ValueDecl *member, DeclNameRef usedName, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowTypeOrInstanceMember(cs, baseType, member, usedName, locator); |
| } |
| |
| bool AllowInvalidPartialApplication::diagnose(const Solution &solution, |
| bool asNote) const { |
| PartialApplicationFailure failure(isWarning(), solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowInvalidPartialApplication * |
| AllowInvalidPartialApplication::create(bool isWarning, ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowInvalidPartialApplication(isWarning, cs, locator); |
| } |
| |
| bool AllowInvalidInitRef::diagnose(const Solution &solution, |
| bool asNote) const { |
| switch (Kind) { |
| case RefKind::DynamicOnMetatype: { |
| InvalidDynamicInitOnMetatypeFailure failure(solution, BaseType, Init, |
| BaseRange, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| case RefKind::ProtocolMetatype: { |
| InitOnProtocolMetatypeFailure failure( |
| solution, BaseType, Init, IsStaticallyDerived, BaseRange, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| case RefKind::NonConstMetatype: { |
| ImplicitInitOnNonConstMetatypeFailure failure(solution, BaseType, Init, |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| } |
| llvm_unreachable("covered switch"); |
| } |
| |
| AllowInvalidInitRef *AllowInvalidInitRef::dynamicOnMetatype( |
| ConstraintSystem &cs, Type baseTy, ConstructorDecl *init, |
| SourceRange baseRange, ConstraintLocator *locator) { |
| return create(RefKind::DynamicOnMetatype, cs, baseTy, init, |
| /*isStaticallyDerived=*/false, baseRange, locator); |
| } |
| |
| AllowInvalidInitRef *AllowInvalidInitRef::onProtocolMetatype( |
| ConstraintSystem &cs, Type baseTy, ConstructorDecl *init, |
| bool isStaticallyDerived, SourceRange baseRange, |
| ConstraintLocator *locator) { |
| return create(RefKind::ProtocolMetatype, cs, baseTy, init, |
| isStaticallyDerived, baseRange, locator); |
| } |
| |
| AllowInvalidInitRef * |
| AllowInvalidInitRef::onNonConstMetatype(ConstraintSystem &cs, Type baseTy, |
| ConstructorDecl *init, |
| ConstraintLocator *locator) { |
| return create(RefKind::NonConstMetatype, cs, baseTy, init, |
| /*isStaticallyDerived=*/false, SourceRange(), locator); |
| } |
| |
| AllowInvalidInitRef * |
| AllowInvalidInitRef::create(RefKind kind, ConstraintSystem &cs, Type baseTy, |
| ConstructorDecl *init, bool isStaticallyDerived, |
| SourceRange baseRange, ConstraintLocator *locator) { |
| return new (cs.getAllocator()) AllowInvalidInitRef( |
| cs, kind, baseTy, init, isStaticallyDerived, baseRange, locator); |
| } |
| |
| bool AllowClosureParamDestructuring::diagnose(const Solution &solution, |
| bool asNote) const { |
| ClosureParamDestructuringFailure failure(solution, ContextualType, |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowClosureParamDestructuring * |
| AllowClosureParamDestructuring::create(ConstraintSystem &cs, |
| FunctionType *contextualType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowClosureParamDestructuring(cs, contextualType, locator); |
| } |
| |
| bool AddMissingArguments::diagnose(const Solution &solution, |
| bool asNote) const { |
| MissingArgumentsFailure failure(solution, getSynthesizedArguments(), |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AddMissingArguments * |
| AddMissingArguments::create(ConstraintSystem &cs, |
| ArrayRef<SynthesizedArg> synthesizedArgs, |
| ConstraintLocator *locator) { |
| unsigned size = totalSizeToAlloc<SynthesizedArg>(synthesizedArgs.size()); |
| void *mem = cs.getAllocator().Allocate(size, alignof(AddMissingArguments)); |
| return new (mem) AddMissingArguments(cs, synthesizedArgs, locator); |
| } |
| |
| bool RemoveExtraneousArguments::diagnose(const Solution &solution, |
| bool asNote) const { |
| ExtraneousArgumentsFailure failure(solution, ContextualType, |
| getExtraArguments(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| bool RemoveExtraneousArguments::isMinMaxNameShadowing( |
| ConstraintSystem &cs, ConstraintLocatorBuilder locator) { |
| auto *anchor = getAsExpr<CallExpr>(locator.getAnchor()); |
| if (!anchor) |
| return false; |
| |
| if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor->getFn())) { |
| if (auto *baseExpr = dyn_cast<DeclRefExpr>(UDE->getBase())) { |
| auto *decl = baseExpr->getDecl(); |
| if (baseExpr->isImplicit() && decl && |
| decl->getName() == cs.getASTContext().Id_self) { |
| auto memberName = UDE->getName(); |
| return memberName.isSimpleName("min") || memberName.isSimpleName("max"); |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| RemoveExtraneousArguments *RemoveExtraneousArguments::create( |
| ConstraintSystem &cs, FunctionType *contextualType, |
| llvm::ArrayRef<IndexedParam> extraArgs, ConstraintLocator *locator) { |
| unsigned size = totalSizeToAlloc<IndexedParam>(extraArgs.size()); |
| void *mem = cs.getAllocator().Allocate(size, alignof(RemoveExtraneousArguments)); |
| return new (mem) |
| RemoveExtraneousArguments(cs, contextualType, extraArgs, locator); |
| } |
| |
| bool MoveOutOfOrderArgument::diagnose(const Solution &solution, |
| bool asNote) const { |
| OutOfOrderArgumentFailure failure(solution, ArgIdx, PrevArgIdx, Bindings, |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| MoveOutOfOrderArgument *MoveOutOfOrderArgument::create( |
| ConstraintSystem &cs, unsigned argIdx, unsigned prevArgIdx, |
| ArrayRef<ParamBinding> bindings, ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| MoveOutOfOrderArgument(cs, argIdx, prevArgIdx, bindings, locator); |
| } |
| |
| bool AllowInaccessibleMember::diagnose(const Solution &solution, |
| bool asNote) const { |
| InaccessibleMemberFailure failure(solution, getMember(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowInaccessibleMember * |
| AllowInaccessibleMember::create(ConstraintSystem &cs, Type baseType, |
| ValueDecl *member, DeclNameRef name, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowInaccessibleMember(cs, baseType, member, name, locator); |
| } |
| |
| bool AllowAnyObjectKeyPathRoot::diagnose(const Solution &solution, |
| bool asNote) const { |
| AnyObjectKeyPathRootFailure failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowAnyObjectKeyPathRoot * |
| AllowAnyObjectKeyPathRoot::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) AllowAnyObjectKeyPathRoot(cs, locator); |
| } |
| |
| bool AllowMultiArgFuncKeyPathMismatch::diagnose(const Solution &solution, |
| bool asNote) const { |
| MultiArgFuncKeyPathFailure failure(solution, functionType, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowMultiArgFuncKeyPathMismatch * |
| AllowMultiArgFuncKeyPathMismatch::create(ConstraintSystem &cs, Type fnType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowMultiArgFuncKeyPathMismatch(cs, fnType, locator); |
| } |
| |
| bool TreatKeyPathSubscriptIndexAsHashable::diagnose(const Solution &solution, |
| bool asNote) const { |
| KeyPathSubscriptIndexHashableFailure failure(solution, NonConformingType, |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| TreatKeyPathSubscriptIndexAsHashable * |
| TreatKeyPathSubscriptIndexAsHashable::create(ConstraintSystem &cs, Type type, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| TreatKeyPathSubscriptIndexAsHashable(cs, type, locator); |
| } |
| |
| bool AllowInvalidRefInKeyPath::diagnose(const Solution &solution, |
| bool asNote) const { |
| switch (Kind) { |
| case RefKind::StaticMember: { |
| InvalidStaticMemberRefInKeyPath failure(solution, Member, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| case RefKind::EnumCase: { |
| InvalidEnumCaseRefInKeyPath failure(solution, Member, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| case RefKind::MutatingGetter: { |
| InvalidMemberWithMutatingGetterInKeyPath failure(solution, Member, |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| case RefKind::Method: |
| case RefKind::Initializer: { |
| InvalidMethodRefInKeyPath failure(solution, Member, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| } |
| llvm_unreachable("covered switch"); |
| } |
| |
| AllowInvalidRefInKeyPath * |
| AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, ValueDecl *member, |
| ConstraintLocator *locator) { |
| // Referencing (instance or static) methods in key path is |
| // not currently allowed. |
| if (isa<FuncDecl>(member)) |
| return AllowInvalidRefInKeyPath::create(cs, RefKind::Method, member, |
| locator); |
| |
| // Referencing enum cases in key path is not currently allowed. |
| if (isa<EnumElementDecl>(member)) { |
| return AllowInvalidRefInKeyPath::create(cs, RefKind::EnumCase, member, |
| locator); |
| } |
| |
| // Referencing initializers in key path is not currently allowed. |
| if (isa<ConstructorDecl>(member)) |
| return AllowInvalidRefInKeyPath::create(cs, RefKind::Initializer, |
| member, locator); |
| |
| // Referencing static members in key path is not currently allowed. |
| if (member->isStatic()) |
| return AllowInvalidRefInKeyPath::create(cs, RefKind::StaticMember, member, |
| locator); |
| |
| if (auto *storage = dyn_cast<AbstractStorageDecl>(member)) { |
| // Referencing members with mutating getters in key path is not |
| // currently allowed. |
| if (storage->isGetterMutating()) |
| return AllowInvalidRefInKeyPath::create(cs, RefKind::MutatingGetter, |
| member, locator); |
| } |
| |
| return nullptr; |
| } |
| |
| AllowInvalidRefInKeyPath * |
| AllowInvalidRefInKeyPath::create(ConstraintSystem &cs, RefKind kind, |
| ValueDecl *member, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowInvalidRefInKeyPath(cs, kind, member, locator); |
| } |
| |
| KeyPathContextualMismatch * |
| KeyPathContextualMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| KeyPathContextualMismatch(cs, lhs, rhs, locator); |
| } |
| |
| bool RemoveAddressOf::diagnose(const Solution &solution, bool asNote) const { |
| InvalidUseOfAddressOf failure(solution, getFromType(), getToType(), |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| RemoveAddressOf *RemoveAddressOf::create(ConstraintSystem &cs, Type lhs, Type rhs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) RemoveAddressOf(cs, lhs, rhs, locator); |
| } |
| |
| RemoveReturn::RemoveReturn(ConstraintSystem &cs, Type resultTy, |
| ConstraintLocator *locator) |
| : ContextualMismatch(cs, FixKind::RemoveReturn, resultTy, |
| cs.getASTContext().TheEmptyTupleType, locator) {} |
| |
| bool RemoveReturn::diagnose(const Solution &solution, bool asNote) const { |
| ExtraneousReturnFailure failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| RemoveReturn *RemoveReturn::create(ConstraintSystem &cs, Type resultTy, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) RemoveReturn(cs, resultTy, locator); |
| } |
| |
| bool CollectionElementContextualMismatch::diagnose(const Solution &solution, |
| bool asNote) const { |
| CollectionElementContextualFailure failure(solution, getFromType(), |
| getToType(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| CollectionElementContextualMismatch * |
| CollectionElementContextualMismatch::create(ConstraintSystem &cs, Type srcType, |
| Type dstType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| CollectionElementContextualMismatch(cs, srcType, dstType, locator); |
| } |
| |
| bool DefaultGenericArgument::coalesceAndDiagnose( |
| const Solution &solution, ArrayRef<ConstraintFix *> fixes, |
| bool asNote) const { |
| llvm::SmallVector<GenericTypeParamType *, 4> missingParams{Param}; |
| |
| for (auto *otherFix : fixes) { |
| if (auto *fix = otherFix->getAs<DefaultGenericArgument>()) |
| missingParams.push_back(fix->Param); |
| } |
| |
| MissingGenericArgumentsFailure failure(solution, missingParams, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| bool DefaultGenericArgument::diagnose(const Solution &solution, |
| bool asNote) const { |
| return coalesceAndDiagnose(solution, {}, asNote); |
| } |
| |
| DefaultGenericArgument * |
| DefaultGenericArgument::create(ConstraintSystem &cs, GenericTypeParamType *param, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) DefaultGenericArgument(cs, param, locator); |
| } |
| |
| SkipUnhandledConstructInResultBuilder * |
| SkipUnhandledConstructInResultBuilder::create(ConstraintSystem &cs, |
| UnhandledNode unhandled, |
| NominalTypeDecl *builder, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| SkipUnhandledConstructInResultBuilder(cs, unhandled, builder, locator); |
| } |
| |
| bool SkipUnhandledConstructInResultBuilder::diagnose(const Solution &solution, |
| bool asNote) const { |
| SkipUnhandledConstructInResultBuilderFailure failure(solution, unhandled, |
| builder, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| bool AllowMutatingMemberOnRValueBase::diagnose(const Solution &solution, |
| bool asNote) const { |
| MutatingMemberRefOnImmutableBase failure(solution, getMember(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowMutatingMemberOnRValueBase * |
| AllowMutatingMemberOnRValueBase::create(ConstraintSystem &cs, Type baseType, |
| ValueDecl *member, DeclNameRef name, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowMutatingMemberOnRValueBase(cs, baseType, member, name, locator); |
| } |
| |
| bool AllowTupleSplatForSingleParameter::diagnose(const Solution &solution, |
| bool asNote) const { |
| InvalidTupleSplatWithSingleParameterFailure failure(solution, ParamType, |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| bool AllowTupleSplatForSingleParameter::attempt( |
| ConstraintSystem &cs, SmallVectorImpl<Param> &args, ArrayRef<Param> params, |
| SmallVectorImpl<SmallVector<unsigned, 1>> &bindings, |
| ConstraintLocatorBuilder locator) { |
| if (params.size() != 1 || args.size() <= 1) |
| return true; |
| |
| const auto ¶m = params.front(); |
| |
| if (param.isInOut() || param.isVariadic() || param.isAutoClosure()) |
| return true; |
| |
| auto paramTy = param.getOldType(); |
| |
| // Parameter type has to be either a tuple (with the same arity as |
| // argument list), or a type variable. |
| if (!(paramTy->is<TupleType>() && |
| paramTy->castTo<TupleType>()->getNumElements() == args.size())) |
| return true; |
| |
| SmallVector<TupleTypeElt, 4> argElts; |
| |
| for (unsigned index : indices(args)) { |
| const auto &arg = args[index]; |
| |
| auto label = arg.getLabel(); |
| auto flags = arg.getParameterFlags(); |
| |
| // In situations where there is a single labeled parameter |
| // we need to form a tuple which omits the label e.g. |
| // |
| // func foo<T>(x: (T, T)) {} |
| // foo(x: 0, 1) |
| // |
| // We'd want to suggest argument list to be `x: (0, 1)` instead |
| // of `(x: 0, 1)` which would be incorrect. |
| if (param.hasLabel() && label == param.getLabel()) { |
| if (index == 0) { |
| label = Identifier(); |
| } else { |
| // If label match anything other than first argument, |
| // this can't be a tuple splat. |
| return true; |
| } |
| } |
| |
| // Tuple can't have `inout` elements. |
| if (flags.isInOut()) |
| return true; |
| |
| argElts.push_back({arg.getPlainType(), label}); |
| } |
| |
| bindings[0].clear(); |
| bindings[0].push_back(0); |
| |
| auto newArgType = TupleType::get(argElts, cs.getASTContext()); |
| |
| args.clear(); |
| args.push_back(AnyFunctionType::Param(newArgType, param.getLabel())); |
| |
| auto *fix = new (cs.getAllocator()) AllowTupleSplatForSingleParameter( |
| cs, paramTy, cs.getConstraintLocator(locator)); |
| |
| return cs.recordFix(fix); |
| } |
| |
| bool DropThrowsAttribute::diagnose(const Solution &solution, |
| bool asNote) const { |
| ThrowingFunctionConversionFailure failure(solution, getFromType(), |
| getToType(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| DropThrowsAttribute *DropThrowsAttribute::create(ConstraintSystem &cs, |
| FunctionType *fromType, |
| FunctionType *toType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| DropThrowsAttribute(cs, fromType, toType, locator); |
| } |
| |
| bool DropAsyncAttribute::diagnose(const Solution &solution, |
| bool asNote) const { |
| AsyncFunctionConversionFailure failure(solution, getFromType(), |
| getToType(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| DropAsyncAttribute *DropAsyncAttribute::create(ConstraintSystem &cs, |
| FunctionType *fromType, |
| FunctionType *toType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| DropAsyncAttribute(cs, fromType, toType, locator); |
| } |
| |
| bool IgnoreContextualType::diagnose(const Solution &solution, |
| bool asNote) const { |
| ContextualFailure failure(solution, getFromType(), getToType(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| IgnoreContextualType *IgnoreContextualType::create(ConstraintSystem &cs, |
| Type resultTy, |
| Type specifiedTy, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| IgnoreContextualType(cs, resultTy, specifiedTy, locator); |
| } |
| |
| bool IgnoreAssignmentDestinationType::diagnose(const Solution &solution, |
| bool asNote) const { |
| auto &cs = getConstraintSystem(); |
| auto *AE = getAsExpr<AssignExpr>(getAnchor()); |
| |
| assert(AE); |
| |
| // Let's check whether this is a situation of chained assignment where |
| // one of the steps in the chain is an assignment to self e.g. |
| // `let _ = { $0 = $0 = 42 }`. Assignment chaining results in |
| // type mismatch between result of the previous assignment and the next. |
| { |
| llvm::SaveAndRestore<AssignExpr *> anchor(AE); |
| |
| do { |
| if (TypeChecker::diagnoseSelfAssignment(AE)) |
| return true; |
| } while ((AE = dyn_cast_or_null<AssignExpr>(cs.getParentExpr(AE)))); |
| } |
| |
| auto CTP = isa<SubscriptExpr>(AE->getDest()) ? CTP_SubscriptAssignSource |
| : CTP_AssignSource; |
| |
| AssignmentTypeMismatchFailure failure( |
| solution, CTP, getFromType(), getToType(), |
| cs.getConstraintLocator(AE->getSrc(), LocatorPathElt::ContextualType())); |
| return failure.diagnose(asNote); |
| } |
| |
| bool IgnoreAssignmentDestinationType::diagnoseForAmbiguity( |
| CommonFixesArray commonFixes) const { |
| auto &cs = getConstraintSystem(); |
| |
| // If all of the types are the same let's try to diagnose |
| // this as if there is no ambiguity. |
| if (ContextualMismatch::diagnoseForAmbiguity(commonFixes)) |
| return true; |
| |
| auto *commonLocator = getLocator(); |
| auto *assignment = castToExpr<AssignExpr>(commonLocator->getAnchor()); |
| |
| auto &solution = *commonFixes.front().first; |
| auto *calleeLocator = solution.getCalleeLocator( |
| solution.getConstraintLocator(assignment->getSrc())); |
| auto overload = solution.getOverloadChoiceIfAvailable(calleeLocator); |
| if (!overload) |
| return false; |
| |
| auto memberName = overload->choice.getName().getBaseName(); |
| auto destType = solution.getType(assignment->getDest()); |
| |
| auto &DE = cs.getASTContext().Diags; |
| // TODO(diagnostics): It might be good to add a tailored diagnostic |
| // for cases like this instead of using "contextual" one. |
| DE.diagnose(assignment->getSrc()->getLoc(), |
| diag::no_candidates_match_result_type, |
| memberName.userFacingName(), |
| solution.simplifyType(destType)->getRValueType()); |
| |
| for (auto &entry : commonFixes) { |
| entry.second->diagnose(*entry.first, /*asNote=*/true); |
| } |
| |
| return true; |
| } |
| |
| IgnoreAssignmentDestinationType * |
| IgnoreAssignmentDestinationType::create(ConstraintSystem &cs, Type sourceTy, |
| Type destTy, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| IgnoreAssignmentDestinationType(cs, sourceTy, destTy, locator); |
| } |
| |
| bool AllowInOutConversion::diagnose(const Solution &solution, |
| bool asNote) const { |
| InOutConversionFailure failure(solution, getFromType(), getToType(), |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowInOutConversion *AllowInOutConversion::create(ConstraintSystem &cs, |
| Type argType, Type paramType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowInOutConversion(cs, argType, paramType, locator); |
| } |
| |
| ExpandArrayIntoVarargs * |
| ExpandArrayIntoVarargs::attempt(ConstraintSystem &cs, Type argType, |
| Type paramType, |
| ConstraintLocatorBuilder builder) { |
| auto *locator = cs.getConstraintLocator(builder); |
| |
| auto argLoc = locator->getLastElementAs<LocatorPathElt::ApplyArgToParam>(); |
| if (!(argLoc && argLoc->getParameterFlags().isVariadic())) |
| return nullptr; |
| |
| auto elementType = cs.isArrayType(argType); |
| if (!elementType) |
| return nullptr; |
| |
| ConstraintSystem::TypeMatchOptions options; |
| options |= ConstraintSystem::TypeMatchFlags::TMF_ApplyingFix; |
| options |= ConstraintSystem::TypeMatchFlags::TMF_GenerateConstraints; |
| |
| auto result = cs.matchTypes(*elementType, paramType, ConstraintKind::Subtype, |
| options, builder); |
| |
| if (result.isFailure()) |
| return nullptr; |
| |
| return new (cs.getAllocator()) |
| ExpandArrayIntoVarargs(cs, argType, paramType, locator); |
| } |
| |
| bool ExpandArrayIntoVarargs::diagnose(const Solution &solution, |
| bool asNote) const { |
| ExpandArrayIntoVarargsFailure failure(solution, getFromType(), getToType(), |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| bool ExplicitlyConstructRawRepresentable::diagnose(const Solution &solution, |
| bool asNote) const { |
| MissingRawRepresentableInitFailure failure(solution, RawReprType, |
| ExpectedType, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| ExplicitlyConstructRawRepresentable * |
| ExplicitlyConstructRawRepresentable::create(ConstraintSystem &cs, |
| Type rawReprType, Type expectedType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) ExplicitlyConstructRawRepresentable( |
| cs, rawReprType, expectedType, locator); |
| } |
| |
| bool UseRawValue::diagnose(const Solution &solution, bool asNote) const { |
| MissingRawValueFailure failure(solution, RawReprType, ExpectedType, |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| UseRawValue *UseRawValue::create(ConstraintSystem &cs, Type rawReprType, |
| Type expectedType, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| UseRawValue(cs, rawReprType, expectedType, locator); |
| } |
| |
| unsigned AllowArgumentMismatch::getParamIdx() const { |
| const auto *locator = getLocator(); |
| auto elt = locator->castLastElementTo<LocatorPathElt::ApplyArgToParam>(); |
| return elt.getParamIdx(); |
| } |
| |
| bool AllowArgumentMismatch::diagnose(const Solution &solution, |
| bool asNote) const { |
| ArgumentMismatchFailure failure(solution, getFromType(), getToType(), |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowArgumentMismatch * |
| AllowArgumentMismatch::create(ConstraintSystem &cs, Type argType, |
| Type paramType, ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowArgumentMismatch(cs, argType, paramType, locator); |
| } |
| |
| bool RemoveInvalidCall::diagnose(const Solution &solution, bool asNote) const { |
| ExtraneousCallFailure failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| RemoveInvalidCall *RemoveInvalidCall::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) RemoveInvalidCall(cs, locator); |
| } |
| |
| bool TreatEphemeralAsNonEphemeral::diagnose(const Solution &solution, |
| bool asNote) const { |
| NonEphemeralConversionFailure failure(solution, getLocator(), getFromType(), |
| getToType(), ConversionKind, |
| isWarning()); |
| return failure.diagnose(asNote); |
| } |
| |
| TreatEphemeralAsNonEphemeral *TreatEphemeralAsNonEphemeral::create( |
| ConstraintSystem &cs, ConstraintLocator *locator, Type srcType, |
| Type dstType, ConversionRestrictionKind conversionKind, |
| bool downgradeToWarning) { |
| return new (cs.getAllocator()) TreatEphemeralAsNonEphemeral( |
| cs, locator, srcType, dstType, conversionKind, downgradeToWarning); |
| } |
| |
| std::string TreatEphemeralAsNonEphemeral::getName() const { |
| std::string name; |
| name += "treat ephemeral as non-ephemeral for "; |
| name += ::getName(ConversionKind); |
| return name; |
| } |
| |
| bool SpecifyBaseTypeForContextualMember::diagnose(const Solution &solution, |
| bool asNote) const { |
| MissingContextualBaseInMemberRefFailure failure(solution, MemberName, |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| SpecifyBaseTypeForContextualMember *SpecifyBaseTypeForContextualMember::create( |
| ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| SpecifyBaseTypeForContextualMember(cs, member, locator); |
| } |
| |
| std::string SpecifyClosureParameterType::getName() const { |
| std::string name; |
| llvm::raw_string_ostream OS(name); |
| |
| auto *closure = castToExpr<ClosureExpr>(getAnchor()); |
| auto paramLoc = |
| getLocator()->castLastElementTo<LocatorPathElt::TupleElement>(); |
| |
| auto *PD = closure->getParameters()->get(paramLoc.getIndex()); |
| |
| OS << "specify type for parameter "; |
| if (PD->isAnonClosureParam()) { |
| OS << "$" << paramLoc.getIndex(); |
| } else { |
| OS << "'" << PD->getParameterName() << "'"; |
| } |
| |
| return OS.str(); |
| } |
| |
| bool SpecifyClosureParameterType::diagnose(const Solution &solution, |
| bool asNote) const { |
| UnableToInferClosureParameterType failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| SpecifyClosureParameterType * |
| SpecifyClosureParameterType::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) SpecifyClosureParameterType(cs, locator); |
| } |
| |
| bool SpecifyClosureReturnType::diagnose(const Solution &solution, |
| bool asNote) const { |
| UnableToInferClosureReturnType failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| SpecifyClosureReturnType * |
| SpecifyClosureReturnType::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) SpecifyClosureReturnType(cs, locator); |
| } |
| |
| bool SpecifyObjectLiteralTypeImport::diagnose(const Solution &solution, |
| bool asNote) const { |
| UnableToInferProtocolLiteralType failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| SpecifyObjectLiteralTypeImport * |
| SpecifyObjectLiteralTypeImport::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) SpecifyObjectLiteralTypeImport(cs, locator); |
| } |
| |
| AllowNonClassTypeToConvertToAnyObject::AllowNonClassTypeToConvertToAnyObject( |
| ConstraintSystem &cs, Type type, ConstraintLocator *locator) |
| : ContextualMismatch(cs, FixKind::AllowNonClassTypeToConvertToAnyObject, |
| type, cs.getASTContext().getAnyObjectType(), locator) { |
| } |
| |
| bool AllowNonClassTypeToConvertToAnyObject::diagnose(const Solution &solution, |
| bool asNote) const { |
| auto *locator = getLocator(); |
| |
| if (locator->isForContextualType()) { |
| ContextualFailure failure(solution, getFromType(), getToType(), locator); |
| return failure.diagnose(asNote); |
| } |
| |
| if (locator->isLastElement<LocatorPathElt::ApplyArgToParam>()) { |
| ArgumentMismatchFailure failure(solution, getFromType(), getToType(), |
| locator); |
| return failure.diagnose(asNote); |
| } |
| |
| return false; |
| } |
| |
| AllowNonClassTypeToConvertToAnyObject * |
| AllowNonClassTypeToConvertToAnyObject::create(ConstraintSystem &cs, Type type, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowNonClassTypeToConvertToAnyObject(cs, type, locator); |
| } |
| |
| bool AddQualifierToAccessTopLevelName::diagnose(const Solution &solution, |
| bool asNote) const { |
| MissingQuialifierInMemberRefFailure failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AddQualifierToAccessTopLevelName * |
| AddQualifierToAccessTopLevelName::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) AddQualifierToAccessTopLevelName(cs, locator); |
| } |
| |
| bool AllowCoercionToForceCast::diagnose(const Solution &solution, |
| bool asNote) const { |
| CoercionAsForceCastFailure failure(solution, getFromType(), getToType(), |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowCoercionToForceCast * |
| AllowCoercionToForceCast::create(ConstraintSystem &cs, Type fromType, |
| Type toType, ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowCoercionToForceCast(cs, fromType, toType, locator); |
| } |
| |
| bool AllowKeyPathRootTypeMismatch::diagnose(const Solution &solution, |
| bool asNote) const { |
| KeyPathRootTypeMismatchFailure failure(solution, getFromType(), getToType(), |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowKeyPathRootTypeMismatch * |
| AllowKeyPathRootTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| AllowKeyPathRootTypeMismatch(cs, lhs, rhs, locator); |
| } |
| |
| SpecifyKeyPathRootType * |
| SpecifyKeyPathRootType::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| SpecifyKeyPathRootType(cs, locator); |
| } |
| |
| bool SpecifyKeyPathRootType::diagnose(const Solution &solution, |
| bool asNote) const { |
| UnableToInferKeyPathRootFailure failure(solution, getLocator()); |
| |
| return failure.diagnose(asNote); |
| } |
| |
| bool UnwrapOptionalBaseKeyPathApplication::diagnose(const Solution &solution, |
| bool asNote) const { |
| MissingOptionalUnwrapKeyPathFailure failure(solution, getFromType(), |
| getToType(), getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| UnwrapOptionalBaseKeyPathApplication * |
| UnwrapOptionalBaseKeyPathApplication::attempt(ConstraintSystem &cs, Type baseTy, |
| Type rootTy, |
| ConstraintLocator *locator) { |
| if(baseTy->hasTypeVariable() || rootTy->hasTypeVariable()) |
| return nullptr; |
| |
| if (!isExpr<SubscriptExpr>(locator->getAnchor())) |
| return nullptr; |
| |
| // Only diagnose this if base is an optional type and we only have a |
| // single level of optionality so we can safely suggest unwrapping. |
| auto nonOptionalTy = baseTy->getOptionalObjectType(); |
| if (!nonOptionalTy || nonOptionalTy->getOptionalObjectType()) |
| return nullptr; |
| |
| auto result = |
| cs.matchTypes(nonOptionalTy, rootTy, ConstraintKind::Subtype, |
| ConstraintSystem::TypeMatchFlags::TMF_ApplyingFix, locator); |
| if (result.isFailure()) |
| return nullptr; |
| |
| return new (cs.getAllocator()) |
| UnwrapOptionalBaseKeyPathApplication(cs, baseTy, rootTy, locator); |
| } |
| |
| bool SpecifyLabelToAssociateTrailingClosure::diagnose(const Solution &solution, |
| bool asNote) const { |
| TrailingClosureRequiresExplicitLabel failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| SpecifyLabelToAssociateTrailingClosure * |
| SpecifyLabelToAssociateTrailingClosure::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| SpecifyLabelToAssociateTrailingClosure(cs, locator); |
| } |
| |
| bool AllowKeyPathWithoutComponents::diagnose(const Solution &solution, |
| bool asNote) const { |
| InvalidEmptyKeyPathFailure failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowKeyPathWithoutComponents * |
| AllowKeyPathWithoutComponents::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) AllowKeyPathWithoutComponents(cs, locator); |
| } |
| |
| bool IgnoreInvalidResultBuilderBody::diagnose(const Solution &solution, |
| bool asNote) const { |
| switch (Phase) { |
| // Handled below |
| case ErrorInPhase::PreCheck: |
| break; |
| case ErrorInPhase::ConstraintGeneration: |
| return true; // Already diagnosed by `matchResultBuilder`. |
| } |
| |
| auto *S = castToExpr<ClosureExpr>(getAnchor())->getBody(); |
| |
| class PreCheckWalker : public ASTWalker { |
| DeclContext *DC; |
| DiagnosticTransaction Transaction; |
| |
| public: |
| PreCheckWalker(DeclContext *dc) |
| : DC(dc), Transaction(dc->getASTContext().Diags) {} |
| |
| std::pair<bool, Expr *> walkToExprPre(Expr *E) override { |
| auto hasError = ConstraintSystem::preCheckExpression( |
| E, DC, /*replaceInvalidRefsWithErrors=*/true); |
| return std::make_pair(false, hasError ? nullptr : E); |
| } |
| |
| std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override { |
| return std::make_pair(true, S); |
| } |
| |
| // Ignore patterns because result builder pre-check does so as well. |
| std::pair<bool, Pattern *> walkToPatternPre(Pattern *P) override { |
| return std::make_pair(false, P); |
| } |
| |
| bool diagnosed() const { |
| return Transaction.hasErrors(); |
| } |
| }; |
| |
| PreCheckWalker walker(solution.getDC()); |
| S->walk(walker); |
| |
| return walker.diagnosed(); |
| } |
| |
| IgnoreInvalidResultBuilderBody *IgnoreInvalidResultBuilderBody::create( |
| ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| IgnoreInvalidResultBuilderBody(cs, phase, locator); |
| } |
| |
| bool SpecifyContextualTypeForNil::diagnose(const Solution &solution, |
| bool asNote) const { |
| MissingContextualTypeForNil failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| SpecifyContextualTypeForNil * |
| SpecifyContextualTypeForNil::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) SpecifyContextualTypeForNil(cs, locator); |
| } |
| |
| bool AllowRefToInvalidDecl::diagnose(const Solution &solution, |
| bool asNote) const { |
| ReferenceToInvalidDeclaration failure(solution, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| AllowRefToInvalidDecl * |
| AllowRefToInvalidDecl::create(ConstraintSystem &cs, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) AllowRefToInvalidDecl(cs, locator); |
| } |
| |
| bool IgnoreResultBuilderWithReturnStmts::diagnose(const Solution &solution, |
| bool asNote) const { |
| InvalidReturnInResultBuilderBody failure(solution, BuilderType, getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| IgnoreResultBuilderWithReturnStmts * |
| IgnoreResultBuilderWithReturnStmts::create(ConstraintSystem &cs, Type builderTy, |
| ConstraintLocator *locator) { |
| return new (cs.getAllocator()) |
| IgnoreResultBuilderWithReturnStmts(cs, builderTy, locator); |
| } |
| |
| bool SpecifyBaseTypeForOptionalUnresolvedMember::diagnose( |
| const Solution &solution, bool asNote) const { |
| MemberMissingExplicitBaseTypeFailure failure(solution, MemberName, |
| getLocator()); |
| return failure.diagnose(asNote); |
| } |
| |
| SpecifyBaseTypeForOptionalUnresolvedMember * |
| SpecifyBaseTypeForOptionalUnresolvedMember::attempt( |
| ConstraintSystem &cs, ConstraintKind kind, Type baseTy, |
| DeclNameRef memberName, FunctionRefKind functionRefKind, |
| MemberLookupResult result, ConstraintLocator *locator) { |
| |
| if (kind != ConstraintKind::UnresolvedValueMember) |
| return nullptr; |
| |
| // None or only one viable candidate, there is no ambiguity. |
| if (result.ViableCandidates.size() <= 1) |
| return nullptr; |
| |
| // Only diagnose those situations for static members. |
| if (!baseTy->is<MetatypeType>()) |
| return nullptr; |
| |
| // Don't diagnose for function members e.g. Foo? = .none(0). |
| if (functionRefKind != FunctionRefKind::Unapplied) |
| return nullptr; |
| |
| Type underlyingBaseType = baseTy->getMetatypeInstanceType(); |
| if (!underlyingBaseType->getNominalOrBoundGenericNominal()) |
| return nullptr; |
| |
| if (!underlyingBaseType->getOptionalObjectType()) |
| return nullptr; |
| |
| auto unwrappedType = underlyingBaseType->lookThroughAllOptionalTypes(); |
| bool allOptionalBaseCandidates = true; |
| auto filterViableCandidates = |
| [&](SmallVector<OverloadChoice, 4> &candidates, |
| SmallVector<OverloadChoice, 4> &viableCandidates, |
| bool &allOptionalBase) { |
| for (OverloadChoice choice : candidates) { |
| if (!choice.isDecl()) |
| continue; |
| |
| auto memberDecl = choice.getDecl(); |
| if (isa<FuncDecl>(memberDecl)) |
| continue; |
| if (memberDecl->isInstanceMember()) |
| continue; |
| |
| allOptionalBase &= bool(choice.getBaseType() |
| ->getMetatypeInstanceType() |
| ->getOptionalObjectType()); |
| |
| if (auto EED = dyn_cast<EnumElementDecl>(memberDecl)) { |
| if (!EED->hasAssociatedValues()) |
| viableCandidates.push_back(choice); |
| } else if (auto VD = dyn_cast<VarDecl>(memberDecl)) { |
| if (unwrappedType->hasTypeVariable() || |
| VD->getInterfaceType()->isEqual(unwrappedType)) |
| viableCandidates.push_back(choice); |
| } |
| } |
| }; |
| |
| SmallVector<OverloadChoice, 4> viableCandidates; |
| filterViableCandidates(result.ViableCandidates, viableCandidates, |
| allOptionalBaseCandidates); |
| |
| // Also none or only one viable candidate after filtering candidates, there is |
| // no ambiguity. |
| if (viableCandidates.size() <= 1) |
| return nullptr; |
| |
| // Right now, name lookup only unwraps a single layer of optionality, which |
| // for cases where base type is a multi-optional type e.g. Foo?? it only |
| // finds optional base candidates. To produce the correct warning we perform |
| // an extra lookup on unwrapped type. |
| if (!allOptionalBaseCandidates) |
| return new (cs.getAllocator()) |
| SpecifyBaseTypeForOptionalUnresolvedMember(cs, memberName, locator); |
| |
| MemberLookupResult unwrappedResult = |
| cs.performMemberLookup(kind, memberName, MetatypeType::get(unwrappedType), |
| functionRefKind, locator, |
| /*includeInaccessibleMembers*/ false); |
| SmallVector<OverloadChoice, 4> unwrappedViableCandidates; |
| filterViableCandidates(unwrappedResult.ViableCandidates, |
| unwrappedViableCandidates, allOptionalBaseCandidates); |
| if (unwrappedViableCandidates.empty()) |
| return nullptr; |
| |
| return new (cs.getAllocator()) |
| SpecifyBaseTypeForOptionalUnresolvedMember(cs, memberName, locator); |
| } |