//===--- 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 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()) 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(anchor)) anchor = assignExpr->getSrc(); auto *coerceExpr = getAsExpr(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(locator->getAnchor())->getNumElements() <= 1); return new (cs.getAllocator()) TreatArrayLiteralAsDictionary(cs, dictionaryTy, arrayTy, locator); }; bool MarkExplicitlyEscaping::diagnose(const Solution &solution, bool asNote) const { AttributedFuncToTypeConversionFailure failure( solution, getFromType(), getToType(), getLocator(), AttributedFuncToTypeConversionFailure::Escaping); return failure.diagnose(asNote); } MarkExplicitlyEscaping * MarkExplicitlyEscaping::create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) { if (locator->isLastElement()) locator = cs.getConstraintLocator( locator, LocatorPathElt::ArgumentAttribute::forEscaping()); return new (cs.getAllocator()) MarkExplicitlyEscaping(cs, lhs, rhs, locator); } bool MarkGlobalActorFunction::diagnose(const Solution &solution, bool asNote) const { DroppedGlobalActorFunctionAttr failure( solution, getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } MarkGlobalActorFunction * MarkGlobalActorFunction::create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) { if (locator->isLastElement()) locator = cs.getConstraintLocator( locator, LocatorPathElt::ArgumentAttribute::forGlobalActor()); return new (cs.getAllocator()) MarkGlobalActorFunction(cs, lhs, rhs, locator); } bool AddSendableAttribute::diagnose(const Solution &solution, bool asNote) const { AttributedFuncToTypeConversionFailure failure( solution, getFromType(), getToType(), getLocator(), AttributedFuncToTypeConversionFailure::Concurrent); return failure.diagnose(asNote); } AddSendableAttribute * AddSendableAttribute::create(ConstraintSystem &cs, FunctionType *fromType, FunctionType *toType, ConstraintLocator *locator) { if (locator->isLastElement()) locator = cs.getConstraintLocator( locator, LocatorPathElt::ArgumentAttribute::forConcurrent()); return new (cs.getAllocator()) AddSendableAttribute( cs, fromType, toType, 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 correctLabels, ConstraintLocator *locator) { unsigned size = totalSizeToAlloc(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); } bool MissingConformance::diagnoseForAmbiguity( CommonFixesArray commonFixes) const { auto *primaryFix = commonFixes.front().second->getAs(); assert(primaryFix); if (llvm::all_of( commonFixes, [&primaryFix]( const std::pair &entry) { return primaryFix->isEqual(entry.second); })) return diagnose(*commonFixes.front().first); // If the location is the same but there are different requirements // involved let's not attempt to diagnose that as an ambiguity. return false; } bool MissingConformance::isEqual(const ConstraintFix *other) const { auto *conformanceFix = other->getAs(); if (!conformanceFix) return false; return IsContextual == conformanceFix->IsContextual && NonConformingType->isEqual(conformanceFix->NonConformingType) && ProtocolType->isEqual(conformanceFix->ProtocolType); } 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 &entry) -> std::pair { auto &solution = *entry.first; auto *fix = static_cast(entry.second); return {solution.simplifyType(fix->getFromType()), solution.simplifyType(fix->getToType())}; }; auto etalonTypes = getTypes(commonFixes.front()); if (llvm::all_of( commonFixes, [&](const std::pair &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); } bool AllowWrappedValueMismatch::diagnose(const Solution &solution, bool asError) const { WrappedValueMismatch failure(solution, getFromType(), getToType(), getLocator()); return failure.diagnoseAsError(); } AllowWrappedValueMismatch *AllowWrappedValueMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) { return new (cs.getAllocator()) AllowWrappedValueMismatch(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> getStructuralTypeContext(const Solution &solution, ConstraintLocator *locator) { if (auto contextualTypeElt = locator->findLast()) { assert(locator->isLastElement() || locator->isLastElement()); auto &cs = solution.getConstraintSystem(); auto anchor = locator->getAnchor(); auto contextualType = cs.getContextualType(anchor); auto exprType = cs.getType(anchor); return std::make_tuple(contextualTypeElt->getPurpose(), exprType, contextualType); } else if (auto argApplyInfo = solution.getFunctionArgApplyInfo(locator)) { return std::make_tuple(CTP_CallArgument, argApplyInfo->getArgType(), argApplyInfo->getParamType()); } else if (auto *coerceExpr = getAsExpr(locator->getAnchor())) { return std::make_tuple(CTP_CoerceOperand, solution.getType(coerceExpr->getSubExpr()), solution.getType(coerceExpr)); } else if (auto *assignExpr = getAsExpr(locator->getAnchor())) { auto CTP = isa(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(locator->getAnchor())) { assert(isa(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 fixes, bool asNote) const { llvm::SmallVector indices; if (isElementMismatch()) indices.push_back(*Index); for (auto fix : fixes) { auto *tupleFix = fix->getAs(); 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() && getToType()->is()) { 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 index) { return new (cs.getAllocator()) AllowTupleTypeMismatch(cs, lhs, rhs, locator, index); } bool AllowFunctionTypeMismatch::coalesceAndDiagnose( const Solution &solution, ArrayRef fixes, bool asNote) const { llvm::SmallVector indices{ParamIndex}; for (auto fix : fixes) { if (auto *fnFix = fix->getAs()) 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()) { 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 mismatches, ConstraintLocator *locator) { unsigned size = totalSizeToAlloc(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 AllowInvalidPropertyWrapperType::diagnose(const Solution &solution, bool asNote) const { InvalidPropertyWrapperType failure(solution, wrapperType, getLocator()); return failure.diagnose(asNote); } AllowInvalidPropertyWrapperType * AllowInvalidPropertyWrapperType::create(ConstraintSystem &cs, Type wrapperType, ConstraintLocator *locator) { return new (cs.getAllocator()) AllowInvalidPropertyWrapperType(cs, wrapperType, locator); } bool RemoveProjectedValueArgument::diagnose(const Solution &solution, bool asNote) const { InvalidProjectedValueArgument failure(solution, wrapperType, param, getLocator()); return failure.diagnose(asNote); } RemoveProjectedValueArgument * RemoveProjectedValueArgument::create(ConstraintSystem &cs, Type wrapperType, ParamDecl *param, ConstraintLocator *locator) { return new (cs.getAllocator()) RemoveProjectedValueArgument(cs, wrapperType, param, 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(); 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 synthesizedArgs, ConstraintLocator *locator) { unsigned size = totalSizeToAlloc(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(locator.getAnchor()); if (!anchor) return false; if (auto *UDE = dyn_cast(anchor->getFn())) { if (auto *baseExpr = dyn_cast(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 extraArgs, ConstraintLocator *locator) { unsigned size = totalSizeToAlloc(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); } bool MoveOutOfOrderArgument::diagnoseForAmbiguity( CommonFixesArray commonFixes) const { auto *primaryFix = commonFixes.front().second->getAs(); assert(primaryFix); if (llvm::all_of( commonFixes, [&primaryFix]( const std::pair &entry) { return primaryFix->isEqual(entry.second); })) return diagnose(*commonFixes.front().first); return false; } bool MoveOutOfOrderArgument::isEqual(const ConstraintFix *other) const { auto OoOFix = other->getAs(); return OoOFix ? ArgIdx == OoOFix->ArgIdx && PrevArgIdx == OoOFix->PrevArgIdx : false; } MoveOutOfOrderArgument *MoveOutOfOrderArgument::create( ConstraintSystem &cs, unsigned argIdx, unsigned prevArgIdx, ArrayRef 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"); } bool AllowInvalidRefInKeyPath::diagnoseForAmbiguity( CommonFixesArray commonFixes) const { auto *primaryFix = commonFixes.front().second->getAs(); assert(primaryFix); if (llvm::all_of( commonFixes, [&primaryFix]( const std::pair &entry) { return primaryFix->isEqual(entry.second); })) { return diagnose(*commonFixes.front().first); } return false; } bool AllowInvalidRefInKeyPath::isEqual(const ConstraintFix *other) const { auto *refFix = other->getAs(); return refFix ? Kind == refFix->Kind && Member == refFix->Member : false; } AllowInvalidRefInKeyPath * AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, ValueDecl *member, ConstraintLocator *locator) { // Referencing (instance or static) methods in key path is // not currently allowed. if (isa(member)) return AllowInvalidRefInKeyPath::create(cs, RefKind::Method, member, locator); // Referencing enum cases in key path is not currently allowed. if (isa(member)) { return AllowInvalidRefInKeyPath::create(cs, RefKind::EnumCase, member, locator); } // Referencing initializers in key path is not currently allowed. if (isa(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(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 fixes, bool asNote) const { llvm::SmallVector missingParams{Param}; for (auto *otherFix : fixes) { if (auto *fix = otherFix->getAs()) 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 &args, ArrayRef params, SmallVectorImpl> &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() && paramTy->castTo()->getNumElements() == args.size())) return true; SmallVector 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(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(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 anchor(AE); do { if (TypeChecker::diagnoseSelfAssignment(AE)) return true; } while ((AE = dyn_cast_or_null(cs.getParentExpr(AE)))); } auto CTP = isa(AE->getDest()) ? CTP_SubscriptAssignSource : CTP_AssignSource; AssignmentTypeMismatchFailure failure( solution, CTP, getFromType(), getToType(), cs.getConstraintLocator(AE->getSrc(), LocatorPathElt::ContextualType(CTP))); 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(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(); 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(); 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(getAnchor()); auto paramLoc = getLocator()->castLastElementTo(); 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()) { 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(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 { return true; // Already diagnosed by `matchResultBuilder`. } IgnoreInvalidResultBuilderBody * IgnoreInvalidResultBuilderBody::create(ConstraintSystem &cs, ConstraintLocator *locator) { return new (cs.getAllocator()) IgnoreInvalidResultBuilderBody(cs, 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()) 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 &candidates, SmallVector &viableCandidates, bool &allOptionalBase) { for (OverloadChoice choice : candidates) { if (!choice.isDecl()) continue; auto memberDecl = choice.getDecl(); if (isa(memberDecl)) continue; if (memberDecl->isInstanceMember()) continue; // Disable this warning for ambiguities related to a // static member lookup in generic context because it's // possible to declare a member with the same name on // a concrete type and in an extension of a protocol // that type conforms to e.g.: // // struct S : P { static var test: S { ... } // // extension P where Self == S { static var test: { ... } } // // And use that in an optional context e.g. passing `.test` // to a parameter of expecting `S?`. if (auto *extension = dyn_cast(memberDecl->getDeclContext())) { if (extension->getSelfProtocolDecl()) { allOptionalBase = false; break; } } allOptionalBase &= bool(choice.getBaseType() ->getMetatypeInstanceType() ->getOptionalObjectType()); if (auto EED = dyn_cast(memberDecl)) { if (!EED->hasAssociatedValues()) viableCandidates.push_back(choice); } else if (auto VD = dyn_cast(memberDecl)) { if (unwrappedType->hasTypeVariable() || VD->getInterfaceType()->isEqual(unwrappedType)) viableCandidates.push_back(choice); } } }; SmallVector 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 unwrappedViableCandidates; filterViableCandidates(unwrappedResult.ViableCandidates, unwrappedViableCandidates, allOptionalBaseCandidates); if (unwrappedViableCandidates.empty()) return nullptr; return new (cs.getAllocator()) SpecifyBaseTypeForOptionalUnresolvedMember(cs, memberName, locator); } AllowCheckedCastCoercibleOptionalType * AllowCheckedCastCoercibleOptionalType::create(ConstraintSystem &cs, Type fromType, Type toType, CheckedCastKind kind, ConstraintLocator *locator) { return new (cs.getAllocator()) AllowCheckedCastCoercibleOptionalType( cs, fromType, toType, kind, locator); } bool AllowCheckedCastCoercibleOptionalType::diagnose(const Solution &solution, bool asNote) const { CoercibleOptionalCheckedCastFailure failure( solution, getFromType(), getToType(), CastKind, getLocator()); return failure.diagnose(asNote); } AllowAlwaysSucceedCheckedCast * AllowAlwaysSucceedCheckedCast::create(ConstraintSystem &cs, Type fromType, Type toType, CheckedCastKind kind, ConstraintLocator *locator) { return new (cs.getAllocator()) AllowAlwaysSucceedCheckedCast(cs, fromType, toType, kind, locator); } bool AllowAlwaysSucceedCheckedCast::diagnose(const Solution &solution, bool asNote) const { AlwaysSucceedCheckedCastFailure failure(solution, getFromType(), getToType(), CastKind, getLocator()); return failure.diagnose(asNote); } // Although function types maybe compile-time convertible because // compiler can emit thunks at SIL to handle the conversion when // required, only convertions that are supported by the runtime are // when types are trivially equal or non-throwing from type is equal // to throwing to type without throwing clause conversions are not // possible at runtime. bool AllowUnsupportedRuntimeCheckedCast::runtimeSupportedFunctionTypeCast( FunctionType *fnFromType, FunctionType *fnToType) { if (fnFromType->isEqual(fnToType)) { return true; } else if (!fnFromType->isThrowing() && fnToType->isThrowing()) { return fnFromType->isEqual( fnToType->getWithoutThrowing()->castTo()); } // Runtime cannot perform such conversion. return false; } AllowUnsupportedRuntimeCheckedCast * AllowUnsupportedRuntimeCheckedCast::attempt(ConstraintSystem &cs, Type fromType, Type toType, CheckedCastKind kind, ConstraintLocator *locator) { auto fnFromType = fromType->getAs(); auto fnToType = toType->getAs(); if (!(fnFromType && fnToType)) return nullptr; if (runtimeSupportedFunctionTypeCast(fnFromType, fnToType)) return nullptr; return new (cs.getAllocator()) AllowUnsupportedRuntimeCheckedCast(cs, fromType, toType, kind, locator); } bool AllowUnsupportedRuntimeCheckedCast::diagnose(const Solution &solution, bool asNote) const { UnsupportedRuntimeCheckedCastFailure failure( solution, getFromType(), getToType(), CastKind, getLocator()); return failure.diagnose(asNote); } bool AllowInvalidStaticMemberRefOnProtocolMetatype::diagnose( const Solution &solution, bool asNote) const { InvalidMemberRefOnProtocolMetatype failure(solution, getLocator()); return failure.diagnose(asNote); } AllowInvalidStaticMemberRefOnProtocolMetatype * AllowInvalidStaticMemberRefOnProtocolMetatype::create( ConstraintSystem &cs, ConstraintLocator *locator) { return new (cs.getAllocator()) AllowInvalidStaticMemberRefOnProtocolMetatype(cs, locator); }