//===--- CSDiagnostics.h - Constraint Diagnostics -------------------------===// // // 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 provides necessary abstractions for constraint system diagnostics. // //===----------------------------------------------------------------------===// #ifndef SWIFT_SEMA_CSDIAGNOSTICS_H #define SWIFT_SEMA_CSDIAGNOSTICS_H #include "Constraint.h" #include "ConstraintSystem.h" #include "OverloadChoice.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/Expr.h" #include "swift/AST/Identifier.h" #include "swift/AST/Types.h" #include "swift/Basic/SourceLoc.h" #include "llvm/ADT/ArrayRef.h" #include namespace swift { namespace constraints { class FunctionArgApplyInfo; /// Base class for all of the possible diagnostics, /// provides most basic information such as location of /// the problem, parent expression and some utility methods. class FailureDiagnostic { ConstraintSystem &CS; ConstraintLocator *Locator; /// The original anchor before any simplification. Expr *RawAnchor; /// Simplified anchor associated with the given locator. Expr *Anchor; /// Indicates whether locator could be simplified /// down to anchor expression. bool HasComplexLocator; public: FailureDiagnostic(ConstraintSystem &cs, ConstraintLocator *locator) : CS(cs), Locator(locator), RawAnchor(locator->getAnchor()) { std::tie(Anchor, HasComplexLocator) = computeAnchor(); } virtual ~FailureDiagnostic(); /// Try to diagnose a problem given affected expression, /// failure location, types and declarations deduced by /// constraint system, and other auxiliary information. /// /// \param asNote In ambiguity cases it's beneficial to /// produce diagnostic as a note instead of an error if possible. /// /// \returns true If the problem has been successfully diagnosed /// and diagnostic message emitted, false otherwise. bool diagnose(bool asNote = false); /// Try to produce an error diagnostic for the problem at hand. /// /// \returns true If anything was diagnosed, false otherwise. virtual bool diagnoseAsError() = 0; /// Instead of producing an error diagnostic, attempt to /// produce a "note" to complement some other diagnostic /// e.g. ambiguity error. virtual bool diagnoseAsNote(); ConstraintSystem &getConstraintSystem() const { return CS; } Expr *getRawAnchor() const { return RawAnchor; } Expr *getAnchor() const { return Anchor; } ConstraintLocator *getLocator() const { return Locator; } Type getType(Expr *expr, bool wantRValue = true) const; Type getType(const TypeLoc &loc, bool wantRValue = true) const; /// Resolve type variables present in the raw type, if any. Type resolveType(Type rawType, bool reconstituteSugar = false, bool wantRValue = true) const { if (!rawType->hasTypeVariable()) { if (reconstituteSugar) rawType = rawType->reconstituteSugar(/*recursive*/ true); return wantRValue ? rawType->getRValueType() : rawType; } auto &cs = getConstraintSystem(); return cs.simplifyTypeImpl(rawType, [&](TypeVariableType *typeVar) -> Type { if (auto fixed = cs.getFixedType(typeVar)) { auto *genericParam = typeVar->getImpl().getGenericParameter(); if (fixed->isHole() && genericParam) return genericParam; return resolveType(fixed, reconstituteSugar, wantRValue); } return cs.getRepresentative(typeVar); }); } template InFlightDiagnostic emitDiagnostic(ArgTypes &&... Args) const; protected: DeclContext *getDC() const { return CS.DC; } ASTContext &getASTContext() const { return CS.getASTContext(); } Optional> getRestrictionForType(Type type) const { for (auto &restriction : CS.ConstraintRestrictions) { if (std::get<0>(restriction)->isEqual(type)) return std::pair( std::get<1>(restriction), std::get<2>(restriction)); } return None; } ValueDecl *getResolvedMemberRef(UnresolvedDotExpr *member) const { auto locator = CS.getConstraintLocator(member, ConstraintLocator::Member); return CS.findResolvedMemberRef(locator); } /// Retrieve overload choice resolved for a given locator /// by the constraint solver. Optional getOverloadChoiceIfAvailable(ConstraintLocator *locator) const { return CS.findSelectedOverloadFor(locator); } /// Retrive the constraint locator for the given anchor and /// path, uniqued and automatically calculate the summary flags ConstraintLocator * getConstraintLocator(Expr *anchor, ArrayRef path) { return CS.getConstraintLocator(anchor, path); } /// \returns true is locator hasn't been simplified down to expression. bool hasComplexLocator() const { return HasComplexLocator; } /// \returns A parent expression if sub-expression is contained anywhere /// in the root expression or `nullptr` otherwise. Expr *findParentExpr(Expr *subExpr) const; /// If given expression is some kind of a member reference e.g. /// `x.foo` or `x[0]` extract and return its base expression. Expr *getBaseExprFor(Expr *anchor) const; /// For a given locator describing an argument application, or a constraint /// within an argument application, returns the argument list for that /// application. If the locator is not for an argument application, or /// the argument list cannot be found, returns \c nullptr. Expr *getArgumentListExprFor(ConstraintLocator *locator) const; /// \returns The overload choice made by the constraint system for the callee /// of a given locator's anchor, or \c None if no such choice can be found. Optional getChoiceFor(ConstraintLocator *) const; /// \returns A new type with all of the type variables associated with /// generic parameters substituted back into being generic parameter type. Type restoreGenericParameters( Type type, llvm::function_ref substitution = [](GenericTypeParamType *, Type) {}); private: /// Compute anchor expression associated with current diagnostic. std::pair computeAnchor() const; }; /// Base class for all of the diagnostics related to generic requirement /// failures, provides common information like failed requirement, /// declaration where such requirement comes from, etc. class RequirementFailure : public FailureDiagnostic { protected: using PathEltKind = ConstraintLocator::PathElementKind; using DiagOnDecl = Diag; using DiagInReference = Diag; using DiagAsNote = Diag; /// If this failure associated with one of the conditional requirements, /// this field would represent conformance where requirement comes from. const ProtocolConformance *Conformance = nullptr; /// The source of the requirement, if available. One exception /// is failure associated with conditional requirement where /// underlying conformance is specialized. GenericSignature Signature; const ValueDecl *AffectedDecl; /// If possible, find application expression associated /// with current generic requirement failure, that helps /// to diagnose failures related to arguments. const ApplyExpr *Apply = nullptr; /// Types associated with requirement constraint this /// failure originates from. Type LHS, RHS; public: RequirementFailure(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) : FailureDiagnostic(cs, locator), Conformance(getConformanceForConditionalReq(locator)), Signature(getSignature(locator)), AffectedDecl(getDeclRef()), LHS(resolveType(lhs)), RHS(resolveType(rhs)) { assert(locator); assert(isConditional() || Signature); assert(AffectedDecl); assert(getRequirementDC() && "Couldn't find where the requirement came from?"); assert(getGenericContext() && "Affected decl not within a generic context?"); if (auto *parentExpr = findParentExpr(getRawAnchor())) Apply = dyn_cast(parentExpr); } unsigned getRequirementIndex() const { auto reqElt = getLocator()->castLastElementTo(); return reqElt.getIndex(); } /// The generic base type where failing requirement comes from. Type getOwnerType() const; /// Generic context associated with the failure. const GenericContext *getGenericContext() const; /// Generic requirement associated with the failure. const Requirement &getRequirement() const; Type getLHS() const { return LHS; } Type getRHS() const { return RHS; } bool diagnoseAsError() override; bool diagnoseAsNote() override; protected: /// Determine whether this is a conditional requirement failure. bool isConditional() const { return bool(Conformance); } /// Check whether this requirement comes from the contextual type /// that root expression is coerced/converted into. bool isFromContextualType() const; /// Retrieve declaration contextual where current /// requirement has been introduced. const DeclContext *getRequirementDC() const; virtual DiagOnDecl getDiagnosticOnDecl() const = 0; virtual DiagInReference getDiagnosticInRereference() const = 0; virtual DiagAsNote getDiagnosticAsNote() const = 0; static bool isOperator(const ApplyExpr *apply) { return isa(apply) || isa(apply) || isa(apply); } /// Determine whether given declaration represents a static /// or instance property/method, excluding operators. static bool isStaticOrInstanceMember(const ValueDecl *decl); private: /// Retrieve declaration associated with failing generic requirement. ValueDecl *getDeclRef() const; /// Retrieve generic signature where this parameter originates from. GenericSignature getSignature(ConstraintLocator *locator); void emitRequirementNote(const Decl *anchor, Type lhs, Type rhs) const; /// If this is a failure in conditional requirement, retrieve /// conformance information. ProtocolConformance * getConformanceForConditionalReq(ConstraintLocator *locator); }; /// Diagnostics for failed conformance checks originating from /// generic requirements e.g. /// ```swift /// struct S {} /// func foo(_ t: T) {} /// foo(S()) /// ``` class MissingConformanceFailure final : public RequirementFailure { public: MissingConformanceFailure(ConstraintSystem &cs, ConstraintLocator *locator, std::pair conformance) : RequirementFailure(cs, conformance.first, conformance.second, locator) { auto reqElt = locator->castLastElementTo(); assert(reqElt.getRequirementKind() == RequirementKind::Conformance || reqElt.getRequirementKind() == RequirementKind::Layout); } bool diagnoseAsError() override; protected: /// Check whether this requirement is associated with one of the /// operator overloads, in cases like that sometimes it makes more /// sense to produce a generic diagnostic about operator reference /// instead of conformance, because it could be something like /// `true + true`, and it doesn't make much sense to suggest to /// add a conformance from one library type to another. bool diagnoseAsAmbiguousOperatorRef(); DiagOnDecl getDiagnosticOnDecl() const override { return (getRequirement().getKind() == RequirementKind::Layout ? diag::type_does_not_conform_anyobject_decl_owner : diag::type_does_not_conform_decl_owner); } DiagInReference getDiagnosticInRereference() const override { return (getRequirement().getKind() == RequirementKind::Layout ? diag::type_does_not_conform_anyobject_in_decl_ref : diag::type_does_not_conform_in_decl_ref); } DiagAsNote getDiagnosticAsNote() const override { return diag::candidate_types_conformance_requirement; } private: bool diagnoseTypeCannotConform(Expr *anchor, Type nonConformingType, Type protocolType) const; }; /// Diagnose failures related to same-type generic requirements, e.g. /// ```swift /// protocol P { /// associatedtype T /// } /// /// struct S : P { /// typealias T = String /// } /// /// func foo(_ t: [U]) where U.T == Int {} /// foo([S()]) /// ``` /// /// `S.T` is not the same type as `Int`, which is required by `foo`. class SameTypeRequirementFailure final : public RequirementFailure { public: SameTypeRequirementFailure(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) : RequirementFailure(cs, lhs, rhs, locator) { auto reqElt = locator->castLastElementTo(); assert(reqElt.getRequirementKind() == RequirementKind::SameType); } protected: DiagOnDecl getDiagnosticOnDecl() const override { return diag::types_not_equal_decl; } DiagInReference getDiagnosticInRereference() const override { return diag::types_not_equal_in_decl_ref; } DiagAsNote getDiagnosticAsNote() const override { return diag::candidate_types_equal_requirement; } }; /// Diagnose failures related to superclass generic requirements, e.g. /// ```swift /// class A { /// } /// /// class B { /// } /// /// func foo(_ t: [T]) where T: A {} /// foo([B()]) /// ``` /// /// `A` is not the superclass of `B`, which is required by `foo`. class SuperclassRequirementFailure final : public RequirementFailure { public: SuperclassRequirementFailure(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) : RequirementFailure(cs, lhs, rhs, locator) { auto reqElt = locator->castLastElementTo(); assert(reqElt.getRequirementKind() == RequirementKind::Superclass); } protected: DiagOnDecl getDiagnosticOnDecl() const override { return diag::types_not_inherited_decl; } DiagInReference getDiagnosticInRereference() const override { return diag::types_not_inherited_in_decl_ref; } DiagAsNote getDiagnosticAsNote() const override { return diag::candidate_types_inheritance_requirement; } }; /// Diagnose errors associated with missing, extraneous /// or incorrect labels supplied by arguments, e.g. /// ```swift /// func foo(q: String, _ a: Int) {} /// foo("ultimate quesiton", a: 42) /// ``` /// Call to `foo` is going to be diagnosed as missing `q:` /// and having extraneous `a:` labels, with appropriate fix-its added. class LabelingFailure final : public FailureDiagnostic { ArrayRef CorrectLabels; public: LabelingFailure(ConstraintSystem &cs, ConstraintLocator *locator, ArrayRef labels) : FailureDiagnostic(cs, locator), CorrectLabels(labels) {} bool diagnoseAsError() override; bool diagnoseAsNote() override; }; /// Diagnose failures related to attempting member access on optional base /// type without optional chaining or force-unwrapping it first. class MemberAccessOnOptionalBaseFailure final : public FailureDiagnostic { DeclNameRef Member; bool ResultTypeIsOptional; public: MemberAccessOnOptionalBaseFailure(ConstraintSystem &cs, ConstraintLocator *locator, DeclNameRef memberName, bool resultOptional) : FailureDiagnostic(cs, locator), Member(memberName), ResultTypeIsOptional(resultOptional) {} bool diagnoseAsError() override; }; /// Diagnose errors associated with rvalues in positions /// where an lvalue is required, such as inout arguments. class RValueTreatedAsLValueFailure final : public FailureDiagnostic { public: RValueTreatedAsLValueFailure(ConstraintSystem &cs, ConstraintLocator *locator) : FailureDiagnostic(cs, locator) {} bool diagnoseAsError() override; bool diagnoseAsNote() override; }; class TrailingClosureAmbiguityFailure final : public FailureDiagnostic { ArrayRef Choices; public: TrailingClosureAmbiguityFailure(ConstraintSystem &cs, Expr *anchor, ArrayRef choices) : FailureDiagnostic(cs, cs.getConstraintLocator(anchor)), Choices(choices) {} bool diagnoseAsError() override { return false; } bool diagnoseAsNote() override; }; /// Diagnose errors related to assignment expressions e.g. /// trying to assign something to immutable value, or trying /// to access mutating member on immutable base. class AssignmentFailure final : public FailureDiagnostic { Expr *DestExpr; SourceLoc Loc; Diag DeclDiagnostic; Diag TypeDiagnostic; public: AssignmentFailure(Expr *destExpr, ConstraintSystem &cs, SourceLoc diagnosticLoc); AssignmentFailure(Expr *destExpr, ConstraintSystem &cs, SourceLoc diagnosticLoc, Diag declDiag, Diag typeDiag) : FailureDiagnostic(cs, cs.getConstraintLocator(destExpr)), DestExpr(destExpr), Loc(diagnosticLoc), DeclDiagnostic(declDiag), TypeDiagnostic(typeDiag) { } bool diagnoseAsError() override; private: /// Given an expression that has a non-lvalue type, dig into it until /// we find the part of the expression that prevents the entire subexpression /// from being mutable. For example, in a sequence like "x.v.v = 42" we want /// to complain about "x" being a let property if "v.v" are both mutable. /// /// \returns The base subexpression that looks immutable (or that can't be /// analyzed any further) along with an OverloadChoice extracted from it if we /// could. std::pair> resolveImmutableBase(Expr *expr) const; static Diag findDeclDiagonstic(ASTContext &ctx, Expr *destExpr); /// Retrive an member reference associated with given member /// looking through dynamic member lookup on the way. Optional getMemberRef(ConstraintLocator *locator) const; }; /// Intended to diagnose any possible contextual failure /// e.g. argument/parameter, closure result, conversions etc. class ContextualFailure : public FailureDiagnostic { ContextualTypePurpose CTP; Type RawFromType, RawToType; public: ContextualFailure(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) : ContextualFailure(cs, cs.getContextualTypePurpose(locator->getAnchor()), lhs, rhs, locator) {} ContextualFailure(ConstraintSystem &cs, ContextualTypePurpose purpose, Type lhs, Type rhs, ConstraintLocator *locator) : FailureDiagnostic(cs, locator), CTP(purpose), RawFromType(lhs), RawToType(rhs) {} Type getFromType() const { return resolve(RawFromType); } Type getToType() const { return resolve(RawToType); } Type getRawFromType() const { return RawFromType; } Type getRawToType() const { return RawToType; } bool diagnoseAsError() override; bool diagnoseAsNote() override; /// If we're trying to convert something to `nil`. bool diagnoseConversionToNil() const; /// Diagnose failed conversion in a `CoerceExpr`. bool diagnoseCoercionToUnrelatedType() const; /// If we're trying to convert something of type "() -> T" to T, /// then we probably meant to call the value. bool diagnoseMissingFunctionCall() const; /// Produce a specialized diagnostic if this is an invalid conversion to Bool. bool diagnoseConversionToBool() const; /// Produce a specialized diagnostic if this is an attempt to initialize /// or convert an array literal to a dictionary e.g. `let _: [String: Int] = ["A", 0]` bool diagnoseConversionToDictionary() const; /// Produce a specialized diagnostic if this is an attempt to throw /// something with doesn't conform to `Error`. bool diagnoseThrowsTypeMismatch() const; /// Produce a specialized diagnostic if this is an attempt to `yield` /// something of incorrect type. bool diagnoseYieldByReferenceMismatch() const; /// Attempt to attach any relevant fix-its to already produced diagnostic. void tryFixIts(InFlightDiagnostic &diagnostic) const; /// Attempts to add fix-its for these two mistakes: /// /// - Passing an integer where a type conforming to RawRepresentable is /// expected, by wrapping the expression in a call to the contextual /// type's initializer /// /// - Passing a type conforming to RawRepresentable where an integer is /// expected, by wrapping the expression in a call to the rawValue /// accessor /// /// - Return true on the fixit is added, false otherwise. /// /// This helps migration with SDK changes. bool tryRawRepresentableFixIts(InFlightDiagnostic &diagnostic, KnownProtocolKind rawRepresentablePrococol) const; /// Attempts to add fix-its for these two mistakes: /// /// - Passing an integer with the right type but which is getting wrapped with /// a different integer type unnecessarily. The fixit removes the cast. /// /// - Passing an integer but expecting different integer type. The fixit adds /// a wrapping cast. /// /// - Return true on the fixit is added, false otherwise. /// /// This helps migration with SDK changes. bool tryIntegerCastFixIts(InFlightDiagnostic &diagnostic) const; protected: /// Try to add a fix-it when converting between a collection and its slice /// type, such as String <-> Substring or (eventually) Array <-> ArraySlice bool trySequenceSubsequenceFixIts(InFlightDiagnostic &diagnostic) const; /// Try to add a fix-it that suggests to explicitly use `as` or `as!` /// to coerce one type to another if type-checker can prove that such /// conversion is possible. bool tryTypeCoercionFixIt(InFlightDiagnostic &diagnostic) const; /// Try to add a fix-it to conform the decl context (if it's a type) to the /// protocol bool tryProtocolConformanceFixIt(InFlightDiagnostic &diagnostic) const; /// Check whether this contextual failure represents an invalid /// conversion from array literal to dictionary. static bool isInvalidDictionaryConversion(ConstraintSystem &cs, Expr *anchor, Type contextualType); private: Type resolve(Type rawType) const { return resolveType(rawType)->getWithoutSpecifierType(); } /// Try to add a fix-it to convert a stored property into a computed /// property void tryComputedPropertyFixIts(Expr *expr) const; bool isIntegerType(Type type) const { return conformsToKnownProtocol( getConstraintSystem(), type, KnownProtocolKind::ExpressibleByIntegerLiteral); } /// Return true if the conversion from fromType to toType is /// an invalid string index operation. bool isIntegerToStringIndexConversion() const; protected: ContextualTypePurpose getContextualTypePurpose() const { return CTP; } static Optional> getDiagnosticFor(ContextualTypePurpose context, bool forProtocol); }; /// Diagnose errors related to converting function type which /// isn't explicitly '@escaping' to some other type. class NoEscapeFuncToTypeConversionFailure final : public ContextualFailure { public: NoEscapeFuncToTypeConversionFailure(ConstraintSystem &cs, Type fromType, Type toType, ConstraintLocator *locator) : ContextualFailure(cs, fromType, toType, locator) {} bool diagnoseAsError() override; private: /// Emit tailored diagnostics for no-escape parameter conversions e.g. /// passing such parameter as an @escaping argument, or trying to /// assign it to a variable which expects @escaping function. bool diagnoseParameterUse() const; }; /// Diagnose failures related to use of the unwrapped optional types, /// which require some type of force-unwrap e.g. "!" or "try!". class MissingOptionalUnwrapFailure final : public ContextualFailure { public: MissingOptionalUnwrapFailure(ConstraintSystem &cs, Type fromType, Type toType, ConstraintLocator *locator) : ContextualFailure(cs, fromType, toType, locator) {} bool diagnoseAsError() override; private: Type getBaseType() const { return resolveType(getFromType(), /*reconstituteSugar=*/true); } Type getUnwrappedType() const { return resolveType(getBaseType()->getOptionalObjectType(), /*reconstituteSugar=*/true); } /// Suggest a default value via `?? ` void offerDefaultValueUnwrapFixIt(DeclContext *DC, Expr *expr) const; /// Suggest a force optional unwrap via `!` void offerForceUnwrapFixIt(Expr *expr) const; }; /// Diagnostics for mismatched generic arguments e.g /// ```swift /// struct F {} /// let _:F = F() /// ``` class GenericArgumentsMismatchFailure final : public ContextualFailure { ArrayRef Mismatches; public: GenericArgumentsMismatchFailure(ConstraintSystem &cs, Type actualType, Type requiredType, ArrayRef mismatches, ConstraintLocator *locator) : ContextualFailure(cs, actualType, requiredType, locator), Mismatches(mismatches) { assert(actualType->is()); assert(requiredType->is()); } bool diagnoseAsError() override; private: void emitNotesForMismatches() { for (unsigned position : Mismatches) { emitNoteForMismatch(position); } } void emitNoteForMismatch(int mismatchPosition); Optional> getDiagnosticFor(ContextualTypePurpose context); /// The actual type being used. BoundGenericType *getActual() const { return getFromType()->castTo(); } /// The type needed by the generic requirement. BoundGenericType *getRequired() const { return getToType()->castTo(); } }; /// Diagnose failures related to conversion between throwing function type /// and non-throwing one e.g. /// /// ```swift /// func foo(_ t: T) throws -> Void {} /// let _: (Int) -> Void = foo // `foo` can't be implictly converted to /// // non-throwing type `(Int) -> Void` /// ``` class ThrowingFunctionConversionFailure final : public ContextualFailure { public: ThrowingFunctionConversionFailure(ConstraintSystem &cs, Type fromType, Type toType, ConstraintLocator *locator) : ContextualFailure(cs, fromType, toType, locator) { auto fnType1 = fromType->castTo(); auto fnType2 = toType->castTo(); assert(fnType1->throws() != fnType2->throws()); } bool diagnoseAsError() override; }; /// Diagnose failures related attempt to implicitly convert types which /// do not support such implicit converstion. /// "as" or "as!" has to be specified explicitly in cases like that. class MissingExplicitConversionFailure final : public ContextualFailure { public: MissingExplicitConversionFailure(ConstraintSystem &cs, Type fromType, Type toType, ConstraintLocator *locator) : ContextualFailure(cs, fromType, toType, locator) {} bool diagnoseAsError() override; private: bool exprNeedsParensBeforeAddingAs(Expr *expr) { auto *DC = getDC(); auto asPG = TypeChecker::lookupPrecedenceGroup( DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc()); if (!asPG) return true; return exprNeedsParensInsideFollowingOperator(DC, expr, asPG); } bool exprNeedsParensAfterAddingAs(Expr *expr, Expr *rootExpr) { auto *DC = getDC(); auto asPG = TypeChecker::lookupPrecedenceGroup( DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc()); if (!asPG) return true; return exprNeedsParensOutsideFollowingOperator(DC, expr, rootExpr, asPG); } }; /// Diagnose failures related to passing value of some type /// to `inout` or pointer parameter, without explicitly specifying `&`. class MissingAddressOfFailure final : public ContextualFailure { public: MissingAddressOfFailure(ConstraintSystem &cs, Type argTy, Type paramTy, ConstraintLocator *locator) : ContextualFailure(cs, argTy, paramTy, locator) {} bool diagnoseAsError() override; }; /// Diagnose extraneous use of address of (`&`) which could only be /// associated with arguments to inout parameters e.g. /// /// ```swift /// struct S {} /// /// var a: S = ... /// var b: S = ... /// /// a = &b /// ``` class InvalidUseOfAddressOf final : public ContextualFailure { public: InvalidUseOfAddressOf(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) : ContextualFailure(cs, lhs, rhs, locator) {} bool diagnoseAsError() override; protected: /// Compute location of the failure for diagnostic. SourceLoc getLoc() const; }; /// Diagnose mismatches relating to tuple destructuring. class TupleContextualFailure final : public ContextualFailure { /// Indices of the tuple elements whose types do not match. llvm::SmallVector Indices; public: TupleContextualFailure(ConstraintSystem &cs, ContextualTypePurpose purpose, Type lhs, Type rhs, llvm::ArrayRef indices, ConstraintLocator *locator) : ContextualFailure(cs, purpose, lhs, rhs, locator), Indices(indices.begin(), indices.end()) { std::sort(Indices.begin(), Indices.end()); assert(getFromType()->is() && getToType()->is()); } bool diagnoseAsError() override; bool isNumElementsMismatch() const { auto lhsTy = getFromType()->castTo(); auto rhsTy = getToType()->castTo(); return lhsTy->getNumElements() != rhsTy->getNumElements(); } }; class FunctionTypeMismatch final : public ContextualFailure { /// Indices of the parameters whose types do not match. llvm::SmallVector Indices; public: FunctionTypeMismatch(ConstraintSystem &cs, ContextualTypePurpose purpose, Type lhs, Type rhs, llvm::ArrayRef indices, ConstraintLocator *locator) : ContextualFailure(cs, purpose, lhs, rhs, locator), Indices(indices.begin(), indices.end()) { std::sort(Indices.begin(), Indices.end()); assert(getFromType()->is() && getToType()->is()); } bool diagnoseAsError() override; }; /// Diagnose situations when @autoclosure argument is passed to @autoclosure /// parameter directly without calling it first. class AutoClosureForwardingFailure final : public FailureDiagnostic { public: AutoClosureForwardingFailure(ConstraintSystem &cs, ConstraintLocator *locator) : FailureDiagnostic(cs, locator) {} bool diagnoseAsError() override; }; /// Diagnose invalid pointer conversions for an autoclosure result type. /// /// \code /// func foo(_ x: @autoclosure () -> UnsafePointer) {} /// /// var i = 0 /// foo(&i) // Invalid conversion to UnsafePointer /// \endcode class AutoClosurePointerConversionFailure final : public ContextualFailure { public: AutoClosurePointerConversionFailure(ConstraintSystem &cs, Type pointeeType, Type pointerType, ConstraintLocator *locator) : ContextualFailure(cs, pointeeType, pointerType, locator) {} bool diagnoseAsError() override; }; /// Diagnose situations when there was an attempt to unwrap entity /// of non-optional type e.g. /// /// ```swift /// let i: Int = 0 /// _ = i! /// /// struct A { func foo() {} } /// func foo(_ a: A) { /// a?.foo() /// } /// ``` class NonOptionalUnwrapFailure final : public FailureDiagnostic { Type BaseType; public: NonOptionalUnwrapFailure(ConstraintSystem &cs, Type baseType, ConstraintLocator *locator) : FailureDiagnostic(cs, locator), BaseType(baseType) {} bool diagnoseAsError() override; }; class MissingCallFailure final : public FailureDiagnostic { public: MissingCallFailure(ConstraintSystem &cs, ConstraintLocator *locator) : FailureDiagnostic(cs, locator) {} bool diagnoseAsError() override; }; class PropertyWrapperReferenceFailure : public ContextualFailure { VarDecl *Property; bool UsingStorageWrapper; public: PropertyWrapperReferenceFailure(ConstraintSystem &cs, VarDecl *property, bool usingStorageWrapper, Type base, Type wrapper, ConstraintLocator *locator) : ContextualFailure(cs, base, wrapper, locator), Property(property), UsingStorageWrapper(usingStorageWrapper) {} VarDecl *getProperty() const { return Property; } Identifier getPropertyName() const { return Property->getName(); } bool usingStorageWrapper() const { return UsingStorageWrapper; } ValueDecl *getReferencedMember() const { auto *locator = getLocator(); if (auto overload = getOverloadChoiceIfAvailable(locator)) return overload->choice.getDeclOrNull(); return nullptr; } }; class ExtraneousPropertyWrapperUnwrapFailure final : public PropertyWrapperReferenceFailure { public: ExtraneousPropertyWrapperUnwrapFailure(ConstraintSystem &cs, VarDecl *property, bool usingStorageWrapper, Type base, Type wrapper, ConstraintLocator *locator) : PropertyWrapperReferenceFailure(cs, property, usingStorageWrapper, base, wrapper, locator) {} bool diagnoseAsError() override; }; class MissingPropertyWrapperUnwrapFailure final : public PropertyWrapperReferenceFailure { public: MissingPropertyWrapperUnwrapFailure(ConstraintSystem &cs, VarDecl *property, bool usingStorageWrapper, Type base, Type wrapper, ConstraintLocator *locator) : PropertyWrapperReferenceFailure(cs, property, usingStorageWrapper, base, wrapper, locator) {} bool diagnoseAsError() override; }; class SubscriptMisuseFailure final : public FailureDiagnostic { public: SubscriptMisuseFailure(ConstraintSystem &cs, ConstraintLocator *locator) : FailureDiagnostic(cs, locator) {} bool diagnoseAsError() override; bool diagnoseAsNote() override; }; class InvalidMemberRefFailure : public FailureDiagnostic { Type BaseType; DeclNameRef Name; public: InvalidMemberRefFailure(ConstraintSystem &cs, Type baseType, DeclNameRef memberName, ConstraintLocator *locator) : FailureDiagnostic(cs, locator), BaseType(baseType->getRValueType()), Name(memberName) {} protected: Type getBaseType() const { return BaseType; } DeclNameRef getName() const { return Name; } }; /// Diagnose situations when member referenced by name is missing /// from the associated base type, e.g. /// /// ```swift /// struct S {} /// func foo(_ s: S) { /// let _: Int = s.foo(1, 2) // expected type is `(Int, Int) -> Int` /// } /// ``` class MissingMemberFailure final : public InvalidMemberRefFailure { public: MissingMemberFailure(ConstraintSystem &cs, Type baseType, DeclNameRef memberName, ConstraintLocator *locator) : InvalidMemberRefFailure(cs, baseType, memberName, locator) {} bool diagnoseAsError() override; private: /// Tailored diagnostics for missing special `@dynamicCallable` methods /// e.g. if caller expects `dynamicallyCall(withKeywordArguments:)` /// overload to be present, but a class marked as `@dynamicCallable` /// defines only `dynamicallyCall(withArguments:)` variant. bool diagnoseForDynamicCallable() const; static DeclName findCorrectEnumCaseName(Type Ty, TypoCorrectionResults &corrections, DeclNameRef memberName); }; /// Diagnose cases where a member only accessible on generic constraints /// requiring conformance to a protocol is used on a value of the /// existential protocol type e.g. /// /// ```swift /// protocol P { /// var foo: Self { get } /// } /// /// func bar(p: X) { /// p.foo /// } /// ``` class InvalidMemberRefOnExistential final : public InvalidMemberRefFailure { public: InvalidMemberRefOnExistential(ConstraintSystem &cs, Type baseType, DeclNameRef memberName, ConstraintLocator *locator) : InvalidMemberRefFailure(cs, baseType, memberName, locator) {} bool diagnoseAsError() override; }; /// Diagnose situations when we use an instance member on a type /// or a type member on an instance /// /// ```swift /// class Bar {} /// /// enum Foo { /// /// static func f() { /// g(Bar()) /// } /// /// func g(_: Bar) {} /// /// } /// ``` class AllowTypeOrInstanceMemberFailure final : public FailureDiagnostic { Type BaseType; ValueDecl *Member; DeclNameRef Name; public: AllowTypeOrInstanceMemberFailure(ConstraintSystem &cs, Type baseType, ValueDecl *member, DeclNameRef name, ConstraintLocator *locator) : FailureDiagnostic(cs, locator), BaseType(baseType->getRValueType()), Member(member), Name(name) { assert(member); } bool diagnoseAsError() override; }; class PartialApplicationFailure final : public FailureDiagnostic { enum RefKind : unsigned { MutatingMethod, SuperInit, SelfInit, }; bool CompatibilityWarning; public: PartialApplicationFailure(bool warning, ConstraintSystem &cs, ConstraintLocator *locator) : FailureDiagnostic(cs, locator), CompatibilityWarning(warning) {} bool diagnoseAsError() override; }; class InvalidInitRefFailure : public FailureDiagnostic { protected: Type BaseType; const ConstructorDecl *Init; SourceRange BaseRange; InvalidInitRefFailure(ConstraintSystem &cs, Type baseTy, const ConstructorDecl *init, SourceRange baseRange, ConstraintLocator *locator) : FailureDiagnostic(cs, locator), BaseType(baseTy), Init(init), BaseRange(baseRange) {} public: bool diagnoseAsError() override = 0; }; /// Diagnose an attempt to construct an object of class type with a metatype /// value without using 'required' initializer: /// /// ```swift /// class C { /// init(value: Int) {} /// } /// /// func make(type: T.Type) -> T { /// return T.init(value: 42) /// } /// ``` class InvalidDynamicInitOnMetatypeFailure final : public InvalidInitRefFailure { public: InvalidDynamicInitOnMetatypeFailure(ConstraintSystem &cs, Type baseTy, const ConstructorDecl *init, SourceRange baseRange, ConstraintLocator *locator) : InvalidInitRefFailure(cs, baseTy, init, baseRange, locator) {} bool diagnoseAsError() override; }; /// Diagnose an attempt to call initializer on protocol metatype: /// /// ```swift /// protocol P { /// init(value: Int) /// } /// /// func make(type: P.Type) -> P { /// return type.init(value: 42) /// } /// ``` class InitOnProtocolMetatypeFailure final : public InvalidInitRefFailure { bool IsStaticallyDerived; public: InitOnProtocolMetatypeFailure(ConstraintSystem &cs, Type baseTy, const ConstructorDecl *init, bool isStaticallyDerived, SourceRange baseRange, ConstraintLocator *locator) : InvalidInitRefFailure(cs, baseTy, init, baseRange, locator), IsStaticallyDerived(isStaticallyDerived) {} bool diagnoseAsError() override; }; /// Diagnose an attempt to construct an instance using non-constant /// metatype base without explictly specifying `init`: /// /// ```swift /// let foo = Int.self /// foo(0) // should be `foo.init(0)` /// ``` class ImplicitInitOnNonConstMetatypeFailure final : public InvalidInitRefFailure { public: ImplicitInitOnNonConstMetatypeFailure(ConstraintSystem &cs, Type baseTy, const ConstructorDecl *init, ConstraintLocator *locator) : InvalidInitRefFailure(cs, baseTy, init, SourceRange(), locator) {} bool diagnoseAsError() override; }; class MissingArgumentsFailure final : public FailureDiagnostic { using Param = AnyFunctionType::Param; SmallVector SynthesizedArgs; public: MissingArgumentsFailure(ConstraintSystem &cs, ArrayRef synthesizedArgs, ConstraintLocator *locator) : FailureDiagnostic(cs, locator), SynthesizedArgs(synthesizedArgs.begin(), synthesizedArgs.end()) { assert(!SynthesizedArgs.empty() && "No missing arguments?!"); } bool diagnoseAsError() override; bool diagnoseAsNote() override; bool diagnoseSingleMissingArgument() const; private: /// If missing arguments come from a closure, /// let's produce tailored diagnostics. bool diagnoseClosure(ClosureExpr *closure); /// Diagnose cases when instead of multiple distinct arguments /// call got a single tuple argument with expected arity/types. bool diagnoseInvalidTupleDestructuring() const; /// Determine whether missing arguments are associated with /// an implicit call to a property wrapper initializer e.g. /// `@Foo(answer: 42) var question = "ultimate question"` bool isPropertyWrapperInitialization() const; /// Gather informatioin associated with expression that represents /// a call - function, arguments, # of arguments and whether it has /// a trailing closure. std::tuple getCallInfo(Expr *anchor) const; /// Transform given argument into format suitable for a fix-it /// text e.g. `[