//===--- 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 "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/Expr.h" #include "swift/AST/Identifier.h" #include "swift/AST/OperatorNameLookup.h" #include "swift/AST/Types.h" #include "swift/Basic/SourceLoc.h" #include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/FixBehavior.h" #include "swift/Sema/OverloadChoice.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 { const Solution &S; ConstraintLocator *Locator; FixBehavior fixBehavior; public: FailureDiagnostic(const Solution &solution, ConstraintLocator *locator, FixBehavior fixBehavior = FixBehavior::Error) : S(solution), Locator(locator), fixBehavior(fixBehavior) {} FailureDiagnostic(const Solution &solution, ASTNode anchor, FixBehavior fixBehavior = FixBehavior::Error) : FailureDiagnostic(solution, solution.getConstraintLocator(anchor), fixBehavior) { } virtual ~FailureDiagnostic(); virtual SourceLoc getLoc() const { return constraints::getLoc(getAnchor()); } virtual SourceRange getSourceRange() const { return constraints::getSourceRange(getAnchor()); } /// 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(); ASTNode getRawAnchor() const { return Locator->getAnchor(); } virtual ASTNode getAnchor() const; ConstraintLocator *getLocator() const { return Locator; } Type getType(ASTNode node, bool wantRValue = true) const; /// Get type associated with a given ASTNode without resolving it, /// which means that returned type would have type variables. Type getRawType(ASTNode node) const; /// Resolve type variables present in the raw type, if any. Type resolveType(Type rawType, bool reconstituteSugar = false, bool wantRValue = true) const; template InFlightDiagnostic emitDiagnostic(ArgTypes &&... Args) const; template InFlightDiagnostic emitDiagnosticAt(ArgTypes &&... Args) const; protected: const Solution &getSolution() const { return S; } ConstraintSystem &getConstraintSystem() const { return S.getConstraintSystem(); } Type getContextualType(ASTNode anchor) const { auto &cs = getConstraintSystem(); return cs.getContextualType(anchor, /*forConstraint=*/false); } TypeLoc getContextualTypeLoc(ASTNode anchor) const { auto &cs = getConstraintSystem(); return cs.getContextualTypeLoc(anchor); } ContextualTypePurpose getContextualTypePurpose(ASTNode anchor) const { auto &cs = getConstraintSystem(); return cs.getContextualTypePurpose(anchor); } DeclContext *getDC() const { auto &cs = getConstraintSystem(); return cs.DC; } ModuleDecl *getParentModule() const { return getDC()->getParentModule(); } ASTContext &getASTContext() const { auto &cs = getConstraintSystem(); return cs.getASTContext(); } /// Retrieve overload choice resolved for a given locator /// by the constraint solver. std::optional getOverloadChoiceIfAvailable(ConstraintLocator *locator) const { return S.getOverloadChoiceIfAvailable(locator); } /// Retrieve the overload choice for the callee associated with the given /// locator, if any. std::optional getCalleeOverloadChoiceIfAvailable(ConstraintLocator *locator) const { return S.getCalleeOverloadChoiceIfAvailable(locator); } ConstraintLocator * getConstraintLocator(ASTNode anchor, ConstraintLocator::PathElement element) const { return S.getConstraintLocator(anchor, {element}); } /// Retrieve the constraint locator for the given anchor and /// path, uniqued and automatically calculate the summary flags ConstraintLocator *getConstraintLocator( ASTNode anchor, ArrayRef path = {}) const { return S.getConstraintLocator(anchor, path); } ConstraintLocator * getConstraintLocator(ConstraintLocator *baseLocator, ConstraintLocator::PathElement element) const { return S.getConstraintLocator(baseLocator, element); } std::optional getFunctionArgApplyInfo(ConstraintLocator *locator) const { return S.getFunctionArgApplyInfo(locator); } /// \returns A parent expression if sub-expression is contained anywhere /// in the root expression or `nullptr` otherwise. Expr *findParentExpr(const 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(const 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. ArgumentList *getArgumentListFor(ConstraintLocator *locator) const; /// \returns A new type with all of the type variables associated with /// generic parameters substituted back into being generic parameter type. Type restoreGenericParameters( Type type, llvm::function_ref substitution = [](GenericTypeParamType *, Type) {}); bool conformsToKnownProtocol(Type type, KnownProtocolKind protocol) const; /// Retrieve an editor placeholder with a given description, or a given /// type if specified. StringRef getEditorPlaceholder(StringRef description, Type ty, llvm::SmallVectorImpl &scratch) 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(const Solution &solution, Type lhs, Type rhs, ConstraintLocator *locator) : FailureDiagnostic(solution, 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 *expr = getAsExpr(getRawAnchor())) if (auto *parentExpr = findParentExpr(expr)) Apply = dyn_cast(parentExpr); } virtual SourceLoc getLoc() const override { return FailureDiagnostic::getLoc(); } 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 maybeEmitRequirementNote(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(const Solution &solution, ConstraintLocator *locator, std::pair conformance) : RequirementFailure(solution, conformance.first, conformance.second, locator) { #ifndef NDEBUG auto reqElt = locator->castLastElementTo(); assert(reqElt.getRequirementKind() == RequirementKind::Conformance || reqElt.getRequirementKind() == RequirementKind::Layout); #endif } virtual SourceLoc getLoc() const override; 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(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(const Solution &solution, Type lhs, Type rhs, ConstraintLocator *locator) : RequirementFailure(solution, lhs, rhs, locator) { #ifndef NDEBUG auto reqElt = locator->castLastElementTo(); assert(reqElt.getRequirementKind() == RequirementKind::SameType); #endif } 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 same-shape generic requirements, e.g. /// ```swift /// func foo(t: T..., u: U...) -> (T, U)... {} /// func bar(t: T..., u: U...) { /// foo(t: t..., u: u...) /// } /// ``` /// /// The generic parameter packs `T` and `U` are not known to have the same /// shape, which is required by `foo()`. class SameShapeRequirementFailure final : public RequirementFailure { public: SameShapeRequirementFailure(const Solution &solution, Type lhs, Type rhs, ConstraintLocator *locator) : RequirementFailure(solution, lhs, rhs, locator) { #ifndef NDEBUG auto reqElt = locator->castLastElementTo(); assert(reqElt.getRequirementKind() == RequirementKind::SameShape); #endif } protected: DiagOnDecl getDiagnosticOnDecl() const override { return diag::types_not_same_shape_decl; } DiagInReference getDiagnosticInRereference() const override { return diag::types_not_same_shape_in_decl_ref; } DiagAsNote getDiagnosticAsNote() const override { return diag::candidate_types_same_shape_requirement; } }; class SameShapeExpansionFailure final : public FailureDiagnostic { Type lhs, rhs; public: SameShapeExpansionFailure(const Solution &solution, Type lhs, Type rhs, ConstraintLocator *locator) : FailureDiagnostic(solution, locator), lhs(resolveType(lhs)), rhs(resolveType(rhs)) {} bool diagnoseAsError() override; }; /// 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(const Solution &solution, Type lhs, Type rhs, ConstraintLocator *locator) : RequirementFailure(solution, lhs, rhs, locator) { #ifndef NDEBUG auto reqElt = locator->castLastElementTo(); assert(reqElt.getRequirementKind() == RequirementKind::Superclass); #endif } 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 question", 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(const Solution &solution, ConstraintLocator *locator, ArrayRef labels) : FailureDiagnostic(solution, locator), CorrectLabels(labels) {} bool diagnoseAsError() override; bool diagnoseAsNote() override; }; /// A diagnostic that will be emitted on the base if its locator points to a /// member access. class MemberReferenceFailure : public FailureDiagnostic { public: MemberReferenceFailure(const Solution &solution, ConstraintLocator *locator) : FailureDiagnostic(solution, locator) {} ASTNode getAnchor() const override; }; /// Diagnose failures related to attempting member access on optional base /// type without optional chaining or force-unwrapping it first. class MemberAccessOnOptionalBaseFailure final : public MemberReferenceFailure { DeclNameRef Member; Type MemberBaseType; bool ResultTypeIsOptional; public: MemberAccessOnOptionalBaseFailure(const Solution &solution, ConstraintLocator *locator, DeclNameRef memberName, Type memberBaseType, bool resultOptional) : MemberReferenceFailure(solution, locator), Member(memberName), MemberBaseType(resolveType(memberBaseType)), ResultTypeIsOptional(resultOptional) {} bool diagnoseAsError() override; Type getMemberBaseType() const { return MemberBaseType; } SourceLoc getLoc() const override { // The end location points to the dot in the member access. return getSourceRange().End; } SourceRange getSourceRange() const override; }; /// Diagnose errors associated with rvalues in positions /// where an lvalue is required, such as inout arguments. class RValueTreatedAsLValueFailure final : public FailureDiagnostic { public: RValueTreatedAsLValueFailure(const Solution &solution, ConstraintLocator *locator) : FailureDiagnostic(solution, locator) {} bool diagnoseAsError() override; bool diagnoseAsNote() override; }; class TrailingClosureAmbiguityFailure final : public FailureDiagnostic { ArrayRef Choices; public: TrailingClosureAmbiguityFailure(ArrayRef solutions, ASTNode anchor, ArrayRef choices) : FailureDiagnostic(solutions.front(), 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, const Solution &solution, SourceLoc diagnosticLoc); AssignmentFailure(Expr *destExpr, const Solution &solution, SourceLoc diagnosticLoc, Diag declDiag, Diag typeDiag) : FailureDiagnostic(solution, 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; std::pair> resolveImmutableBase(const Expr *expr) const { return resolveImmutableBase(const_cast(expr)); } static Diag findDeclDiagnostic(ASTContext &ctx, const Expr *destExpr); /// Retrieve an member reference associated with given member /// looking through dynamic member lookup on the way. std::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(const Solution &solution, Type lhs, Type rhs, ConstraintLocator *locator, FixBehavior fixBehavior = FixBehavior::Error) : ContextualFailure( solution, locator->isForContextualType() ? locator->castLastElementTo() .getPurpose() : solution.getConstraintSystem().getContextualTypePurpose( locator->getAnchor()), lhs, rhs, locator, fixBehavior) {} ContextualFailure(const Solution &solution, ContextualTypePurpose purpose, Type lhs, Type rhs, ConstraintLocator *locator, FixBehavior fixBehavior = FixBehavior::Error) : FailureDiagnostic(solution, locator, fixBehavior), CTP(purpose), RawFromType(lhs), RawToType(rhs) { assert(lhs && "Expected a valid 'from' type"); assert(rhs && "Expected a valid 'to' type"); } SourceLoc getLoc() const override; 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; /// 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 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 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; private: Type resolve(Type rawType) const { return resolveType(rawType)->getWithoutSpecifierType(); } bool isIntegerType(Type type) const { return conformsToKnownProtocol( 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 std::optional> getDiagnosticFor(ContextualTypePurpose context, Type contextualType); protected: bool exprNeedsParensBeforeAddingAs(const Expr *expr, DeclContext *DC) const { auto asPG = TypeChecker::lookupPrecedenceGroup( DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc()) .getSingle(); if (!asPG) return true; return exprNeedsParensInsideFollowingOperator(DC, const_cast(expr), asPG); } bool exprNeedsParensAfterAddingAs(const Expr *expr, DeclContext *DC) const { auto asPG = TypeChecker::lookupPrecedenceGroup( DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc()) .getSingle(); if (!asPG) return true; return exprNeedsParensOutsideFollowingOperator( DC, const_cast(expr), asPG, [&](auto *E) { return findParentExpr(E); }); } }; class NonClassTypeToAnyObjectConversionFailure final : public ContextualFailure { public: NonClassTypeToAnyObjectConversionFailure(const Solution &solution, Type lhs, Type rhs, ConstraintLocator *locator) : ContextualFailure(solution, lhs, rhs, locator, FixBehavior::Error) {} bool diagnoseAsError() override; bool diagnoseAsNote() override; }; /// Diagnose errors related to using an array literal where a /// dictionary is expected. class ArrayLiteralToDictionaryConversionFailure final : public ContextualFailure { public: ArrayLiteralToDictionaryConversionFailure(const Solution &solution, Type arrayTy, Type dictTy, ConstraintLocator *locator) : ContextualFailure(solution, arrayTy, dictTy, locator) {} bool diagnoseAsError() override; }; /// Diagnose errors related to converting function type which /// isn't explicitly '@escaping' or '@Sendable' to some other type. class AttributedFuncToTypeConversionFailure final : public ContextualFailure { public: enum AttributeKind { Escaping, Concurrent, }; const AttributeKind attributeKind; AttributedFuncToTypeConversionFailure(const Solution &solution, Type fromType, Type toType, ConstraintLocator *locator, AttributeKind attributeKind, FixBehavior fixBehavior = FixBehavior::Error) : ContextualFailure(solution, fromType, toType, locator, fixBehavior), attributeKind(attributeKind) {} bool diagnoseAsError() override; private: /// Emit tailored diagnostics for no-escape/non-sendable parameter /// conversions e.g. passing such parameter as an @escaping or @Sendable /// argument, or trying to assign it to a variable which expects @escaping /// or @Sendable function. bool diagnoseParameterUse() const; /// Emit a tailored diagnostic for a no-escape/espace mismatch for function /// arguments where the mismatch has to take into account that a /// function type subtype relation in the parameter position is contravariant. bool diagnoseFunctionParameterEscapenessMismatch(AssignExpr *) const; }; /// Diagnose failure where a global actor attribute is dropped when /// trying to convert one function type to another. class DroppedGlobalActorFunctionAttr final : public ContextualFailure { public: DroppedGlobalActorFunctionAttr(const Solution &solution, Type fromType, Type toType, ConstraintLocator *locator, FixBehavior fixBehavior) : ContextualFailure(solution, fromType, toType, locator, fixBehavior) { } bool diagnoseAsError() override; }; /// 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(const Solution &solution, Type fromType, Type toType, ConstraintLocator *locator) : ContextualFailure(solution, 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, const Expr *expr) const; /// Suggest a force optional unwrap via `!` void offerForceUnwrapFixIt(const Expr *expr) const; }; class WrappedValueMismatch final : public ContextualFailure { public: WrappedValueMismatch(const Solution &solution, Type fromType, Type toType, ConstraintLocator *locator) : ContextualFailure(solution, fromType, toType, locator) { } bool diagnoseAsError() override; }; /// Diagnostics for mismatched generic arguments e.g /// ```swift /// struct F {} /// let _:F = F() /// ``` class GenericArgumentsMismatchFailure final : public ContextualFailure { ArrayRef Mismatches; public: GenericArgumentsMismatchFailure(const Solution &solution, Type actualType, Type requiredType, ArrayRef mismatches, ConstraintLocator *locator) : ContextualFailure(solution, 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); std::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 implicitly converted to /// // non-throwing type `(Int) -> Void` /// ``` class ThrowingFunctionConversionFailure final : public ContextualFailure { public: ThrowingFunctionConversionFailure(const Solution &solution, Type fromType, Type toType, ConstraintLocator *locator) : ContextualFailure(solution, fromType, toType, locator) { #ifndef NDEBUG auto fnType1 = fromType->castTo(); auto fnType2 = toType->castTo(); assert(fnType1->isThrowing() != fnType2->isThrowing()); #endif } bool diagnoseAsError() override; }; /// Diagnose failures related to conversion between the thrown error type /// of two function types, e.g., /// /// ```swift /// func foo(_ t: T) throws(MyError) -> Void {} /// let _: (Int) throws (OtherError)-> Void = foo /// // `MyError` can't be implicitly converted to `OtherError` /// ``` class ThrownErrorTypeConversionFailure final : public ContextualFailure { public: ThrownErrorTypeConversionFailure(const Solution &solution, Type fromType, Type toType, ConstraintLocator *locator) : ContextualFailure(solution, fromType, toType, locator) { } bool diagnoseAsError() override; }; /// Diagnose failures related to conversion between 'async' function type /// and a synchronous one e.g. /// /// ```swift /// func foo(_ t: T) async -> Void {} /// let _: (Int) -> Void = foo // `foo` can't be implicitly converted to /// // synchronous function type `(Int) -> Void` /// ``` class AsyncFunctionConversionFailure final : public ContextualFailure { public: AsyncFunctionConversionFailure(const Solution &solution, Type fromType, Type toType, ConstraintLocator *locator) : ContextualFailure(solution, fromType, toType, locator) { #ifndef NDEBUG auto fnType1 = fromType->castTo(); auto fnType2 = toType->castTo(); assert(fnType1->isAsync() != fnType2->isAsync()); #endif } bool diagnoseAsError() override; }; /// Diagnose failures related attempt to implicitly convert types which /// do not support such implicit conversion. /// "as" or "as!" has to be specified explicitly in cases like that. class MissingExplicitConversionFailure final : public ContextualFailure { public: MissingExplicitConversionFailure(const Solution &solution, Type fromType, Type toType, ConstraintLocator *locator) : ContextualFailure(solution, fromType, toType, locator) {} ASTNode getAnchor() const override; bool diagnoseAsError() override; }; /// Diagnose failures related to passing value of some type /// to `inout` or pointer parameter, without explicitly specifying `&`. class MissingAddressOfFailure final : public ContextualFailure { public: MissingAddressOfFailure(const Solution &solution, Type argTy, Type paramTy, ConstraintLocator *locator) : ContextualFailure(solution, 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(const Solution &solution, Type lhs, Type rhs, ConstraintLocator *locator) : ContextualFailure(solution, lhs, rhs, locator) {} bool diagnoseAsError() override; protected: /// Compute location of the failure for diagnostic. SourceLoc getLoc() const override; }; /// 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(const Solution &solution, ContextualTypePurpose purpose, Type lhs, Type rhs, llvm::ArrayRef indices, ConstraintLocator *locator) : ContextualFailure(solution, 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(const Solution &solution, ContextualTypePurpose purpose, Type lhs, Type rhs, llvm::ArrayRef indices, ConstraintLocator *locator) : ContextualFailure(solution, purpose, lhs, rhs, locator), Indices(indices.begin(), indices.end()) { std::sort(Indices.begin(), Indices.end()); assert(getFromType()->is() && getToType()->is()); } 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(const Solution &solution, Type pointeeType, Type pointerType, ConstraintLocator *locator) : ContextualFailure(solution, 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(const Solution &solution, Type baseType, ConstraintLocator *locator) : FailureDiagnostic(solution, locator), BaseType(baseType) {} bool diagnoseAsError() override; }; class MissingCallFailure final : public FailureDiagnostic { /// Try to add a fix-it to convert a stored property into a computed /// property void tryComputedPropertyFixIts() const; public: MissingCallFailure(const Solution &solution, ConstraintLocator *locator) : FailureDiagnostic(solution, locator) {} ASTNode getAnchor() const override; bool diagnoseAsError() override; }; class PropertyWrapperReferenceFailure : public ContextualFailure { VarDecl *Property; bool UsingProjection; public: PropertyWrapperReferenceFailure(const Solution &solution, VarDecl *property, bool usingProjection, Type base, Type wrapper, ConstraintLocator *locator) : ContextualFailure(solution, base, wrapper, locator), Property(property), UsingProjection(usingProjection) {} ASTNode getAnchor() const override; VarDecl *getProperty() const { return Property; } Identifier getPropertyName() const { return Property->getName(); } bool usingProjection() const { return UsingProjection; } ValueDecl *getReferencedMember() const { auto *locator = getLocator(); if (auto overload = getOverloadChoiceIfAvailable(locator)) return overload->choice.getDeclOrNull(); return nullptr; } }; class ExtraneousPropertyWrapperUnwrapFailure final : public PropertyWrapperReferenceFailure { public: ExtraneousPropertyWrapperUnwrapFailure(const Solution &solution, VarDecl *property, bool usingStorageWrapper, Type base, Type wrapper, ConstraintLocator *locator) : PropertyWrapperReferenceFailure(solution, property, usingStorageWrapper, base, wrapper, locator) {} bool diagnoseAsError() override; }; class MissingPropertyWrapperUnwrapFailure final : public PropertyWrapperReferenceFailure { public: MissingPropertyWrapperUnwrapFailure(const Solution &solution, VarDecl *property, bool usingStorageWrapper, Type base, Type wrapper, ConstraintLocator *locator) : PropertyWrapperReferenceFailure(solution, property, usingStorageWrapper, base, wrapper, locator) {} bool diagnoseAsError() override; }; class InvalidPropertyWrapperType final : public FailureDiagnostic { Type wrapperType; public: InvalidPropertyWrapperType(const Solution &solution, Type wrapper, ConstraintLocator *locator) : FailureDiagnostic(solution, locator), wrapperType(resolveType(wrapper)) {} bool diagnoseAsError() override; }; class InvalidProjectedValueArgument final : public FailureDiagnostic { Type wrapperType; ParamDecl *param; public: InvalidProjectedValueArgument(const Solution &solution, Type wrapper, ParamDecl *param, ConstraintLocator *locator) : FailureDiagnostic(solution, locator), wrapperType(resolveType(wrapper)), param(param) {} bool diagnoseAsError() override; }; class SubscriptMisuseFailure final : public FailureDiagnostic { public: SubscriptMisuseFailure(const Solution &solution, ConstraintLocator *locator) : FailureDiagnostic(solution, locator) {} bool diagnoseAsError() override; bool diagnoseAsNote() override; }; class InvalidMemberRefFailure : public MemberReferenceFailure { Type BaseType; DeclNameRef Name; public: InvalidMemberRefFailure(const Solution &solution, Type baseType, DeclNameRef memberName, ConstraintLocator *locator) : MemberReferenceFailure(solution, 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 : public InvalidMemberRefFailure { public: MissingMemberFailure(const Solution &solution, Type baseType, DeclNameRef memberName, ConstraintLocator *locator) : InvalidMemberRefFailure(solution, baseType, memberName, locator) {} SourceLoc getLoc() const override { auto *locator = getLocator(); if (locator->findLast()) { return constraints::getLoc(getAnchor()); } // Diagnostic should point to the member instead of its base expression. return constraints::getLoc(getRawAnchor()); } 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; /// Diagnose methods that return unsafe projections and suggest fixits. /// For example, if Swift cannot find "vector::data" because it is unsafe, try /// to diagnose this and tell the user why we did not import "vector::data". /// /// Provides fixits for: /// at -> subscript /// begin, end -> makeIterator /// front, back -> first, last void diagnoseUnsafeCxxMethod(SourceLoc loc, ASTNode anchor, Type baseType, DeclName name) const; /// Tailored diagnostics for collection literal with unresolved member expression /// that defaults the element type. e.g. _ = [.e] bool diagnoseInLiteralCollectionContext() const; /// Tailored diagnostics for missing subscript member on a tuple base type. /// e.g /// ```swift /// let tuple: (Int, Int) = (0, 0) /// _ = tuple[0] // -> tuple.0. /// ``` bool diagnoseForSubscriptMemberWithTupleBase() const; static DeclName findCorrectEnumCaseName(Type Ty, TypoCorrectionResults &corrections, DeclNameRef memberName); static ValueDecl *findImportedCaseWithMatchingSuffix(Type instanceTy, DeclNameRef name); }; class UnintendedExtraGenericParamMemberFailure final : public MissingMemberFailure { Identifier ParamName; public: UnintendedExtraGenericParamMemberFailure(const Solution &solution, Type baseType, DeclNameRef memberName, Identifier paramName, ConstraintLocator *locator) : MissingMemberFailure(solution, baseType, memberName, locator), ParamName(paramName) {} bool diagnoseAsError() override; }; /// Diagnose cases where a protocol member cannot be accessed with an /// existential, e.g. due to occurrences of `Self` in non-covariant position in /// the type of the member reference: /// /// ```swift /// struct G {} /// protocol P { /// func foo() -> G /// } /// /// func bar(p: any P) { /// p.foo() /// } /// ``` class InvalidMemberRefOnExistential final : public InvalidMemberRefFailure { public: InvalidMemberRefOnExistential(const Solution &solution, Type baseType, DeclNameRef memberName, ConstraintLocator *locator) : InvalidMemberRefFailure(solution, 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 MemberReferenceFailure { Type BaseType; ValueDecl *Member; DeclNameRef Name; public: AllowTypeOrInstanceMemberFailure(const Solution &solution, Type baseType, ValueDecl *member, DeclNameRef name, ConstraintLocator *locator) : MemberReferenceFailure(solution, locator), BaseType(baseType->getRValueType()), Member(member), Name(name) { assert(member); } bool diagnoseAsError() override; }; class PartialApplicationFailure final : public FailureDiagnostic { enum RefKind : unsigned { MutatingMethod, SuperInit, SelfInit, SuperMethod, }; bool CompatibilityWarning; public: PartialApplicationFailure(bool warning, const Solution &solution, ConstraintLocator *locator) : FailureDiagnostic(solution, locator), CompatibilityWarning(warning) {} bool diagnoseAsError() override; }; class InvalidInitRefFailure : public FailureDiagnostic { protected: Type BaseType; const ConstructorDecl *Init; SourceRange BaseRange; ASTNode getAnchor() const override { return getRawAnchor(); } InvalidInitRefFailure(const Solution &solution, Type baseTy, const ConstructorDecl *init, SourceRange baseRange, ConstraintLocator *locator) : FailureDiagnostic(solution, 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(const Solution &solution, Type baseTy, const ConstructorDecl *init, SourceRange baseRange, ConstraintLocator *locator) : InvalidInitRefFailure(solution, 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(const Solution &solution, Type baseTy, const ConstructorDecl *init, bool isStaticallyDerived, SourceRange baseRange, ConstraintLocator *locator) : InvalidInitRefFailure(solution, baseTy, init, baseRange, locator), IsStaticallyDerived(isStaticallyDerived) {} bool diagnoseAsError() override; }; /// Diagnose an attempt to construct an instance using non-constant /// metatype base without explicitly specifying `init`: /// /// ```swift /// let foo = Int.self /// foo(0) // should be `foo.init(0)` /// ``` class ImplicitInitOnNonConstMetatypeFailure final : public InvalidInitRefFailure { public: ImplicitInitOnNonConstMetatypeFailure(const Solution &solution, Type baseTy, const ConstructorDecl *init, ConstraintLocator *locator) : InvalidInitRefFailure(solution, baseTy, init, SourceRange(), locator) {} SourceLoc getLoc() const override; bool diagnoseAsError() override; }; class MissingArgumentsFailure final : public FailureDiagnostic { SmallVector SynthesizedArgs; public: MissingArgumentsFailure(const Solution &solution, ArrayRef synthesizedArgs, ConstraintLocator *locator) : FailureDiagnostic(solution, locator), SynthesizedArgs(synthesizedArgs.begin(), synthesizedArgs.end()) { assert(!SynthesizedArgs.empty() && "No missing arguments?!"); } SourceLoc getLoc() const override; ASTNode getAnchor() const override; bool diagnoseAsError() override; bool diagnoseAsNote() override; bool diagnoseSingleMissingArgument() const; private: /// If missing arguments come from a closure, /// let's produce tailored diagnostics. bool diagnoseClosure(const ClosureExpr *closure); /// Diagnose a single missing argument to a buildBlock call. bool diagnoseMissingResultBuilderElement() const; /// 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 information associated with expression that represents /// a call - function and argument list. std::optional> getCallInfo(ASTNode anchor) const; /// Transform given argument into format suitable for a fix-it /// text e.g. `[