blob: a182b25e4e24c49472b788aa21a7806643d63e28 [file] [log] [blame]
//===--- CSFix.h - 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 provides necessary abstractions for constraint fixes.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SEMA_CSFIX_H
#define SWIFT_SEMA_CSFIX_H
#include "swift/AST/ASTNode.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Expr.h"
#include "swift/AST/Identifier.h"
#include "swift/AST/Type.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Debug.h"
#include "swift/Sema/ConstraintLocator.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/TrailingObjects.h"
#include <string>
namespace llvm {
class raw_ostream;
}
namespace swift {
class SourceManager;
namespace constraints {
class OverloadChoice;
class ConstraintSystem;
class ConstraintLocator;
class ConstraintLocatorBuilder;
enum class ConversionRestrictionKind;
class Solution;
struct MemberLookupResult;
/// Describes the kind of fix to apply to the given constraint before
/// visiting it.
///
/// Note: values 0 and 1 are reserved for empty and tombstone kinds.
enum class FixKind : uint8_t {
/// Introduce a '!' to force an optional unwrap.
ForceOptional = 2,
/// Unwrap an optional base when we have a member access.
UnwrapOptionalBase,
UnwrapOptionalBaseWithOptionalResult,
/// Append 'as! T' to force a downcast to the specified type.
ForceDowncast,
/// Introduce a '&' to take the address of an lvalue.
AddressOf,
/// Remove extraneous use of `&`.
RemoveAddressOf,
/// Replace a coercion ('as') with a forced checked cast ('as!').
CoerceToCheckedCast,
/// Mark function type as explicitly '@escaping'.
ExplicitlyEscaping,
/// Arguments have labeling failures - missing/extraneous or incorrect
/// labels attached to the, fix it by suggesting proper labels.
RelabelArguments,
/// Treat rvalue as lvalue
TreatRValueAsLValue,
/// Add a new conformance to the type to satisfy a requirement.
AddConformance,
/// Skip same-type generic requirement constraint,
/// and assume that types are equal.
SkipSameTypeRequirement,
/// Skip superclass generic requirement constraint,
/// and assume that types are related.
SkipSuperclassRequirement,
/// Fix up one of the sides of conversion to make it seem
/// like the types are aligned.
ContextualMismatch,
/// Fix up the generic arguments of two types so they match each other.
GenericArgumentsMismatch,
/// Fix up @autoclosure argument to the @autoclosure parameter,
/// to for a call to be able to foward it properly, since
/// @autoclosure conversions are unsupported starting from
/// Swift version 5.
AutoClosureForwarding,
/// Remove `!` or `?` because base is not an optional type.
RemoveUnwrap,
/// Add explicit `()` at the end of function or member to call it.
InsertCall,
/// Add '$' or '_' to refer to the property wrapper or storage instead
/// of the wrapped property type.
UsePropertyWrapper,
/// Remove '$' or '_' to refer to the wrapped property type instead of
/// the storage or property wrapper.
UseWrappedValue,
/// Instead of spelling out `subscript` directly, use subscript operator.
UseSubscriptOperator,
/// Requested name is not associated with a give base type,
/// fix this issue by pretending that member exists and matches
/// given arguments/result types exactly.
DefineMemberBasedOnUse,
/// Allow access to type member on instance or instance member on type
AllowTypeOrInstanceMember,
/// Allow expressions where 'mutating' method is only partially applied,
/// which means either not applied at all e.g. `Foo.bar` or only `Self`
/// is applied e.g. `foo.bar` or `Foo.bar(&foo)`.
///
/// Allow expressions where initializer call (either `self.init` or
/// `super.init`) is only partially applied.
AllowInvalidPartialApplication,
/// Non-required constructors may not be not inherited. Therefore when
/// constructing a class object, either the metatype must be statically
/// derived (rather than an arbitrary value of metatype type) or the
/// referenced constructor must be required.
AllowInvalidInitRef,
/// Allow a tuple to be destructured with mismatched arity, or mismatched
/// types.
AllowTupleTypeMismatch,
/// Allow a function type to be destructured with mismatched parameter types
/// or return type.
AllowFunctionTypeMismatch,
/// Allow an invalid member access on a value of protocol type as if
/// that protocol type were a generic constraint requiring conformance
/// to that protocol.
AllowMemberRefOnExistential,
/// If there are fewer arguments than parameters, let's fix that up
/// by adding new arguments to the list represented as type variables.
AddMissingArguments,
/// If there are more arguments than parameters, let's fix that up
/// by removing extraneous arguments.
RemoveExtraneousArguments,
/// Allow single tuple closure parameter destructuring into N arguments.
AllowClosureParameterDestructuring,
/// If there is out-of-order argument, let's fix that by re-ordering.
MoveOutOfOrderArgument,
/// If there is a matching inaccessible member - allow it as if there
/// no access control.
AllowInaccessibleMember,
/// Allow KeyPaths to use AnyObject as root type
AllowAnyObjectKeyPathRoot,
/// Using subscript references in the keypath requires that each
/// of the index arguments to be Hashable.
TreatKeyPathSubscriptIndexAsHashable,
/// Allow an invalid reference to a member declaration as part
/// of a key path component.
AllowInvalidRefInKeyPath,
/// Remove `return` or default last expression of single expression
/// function to `Void` to conform to expected result type.
RemoveReturn,
/// Default ambiguous generic arguments to \c Any
DefaultGenericArgument,
/// Skip any unhandled constructs that occur within a closure argument that
/// matches up with a parameter that has a result builder.
SkipUnhandledConstructInResultBuilder,
/// Allow invalid reference to a member declared as `mutating`
/// when base is an r-value type.
AllowMutatingMemberOnRValueBase,
/// Allow a single tuple parameter to be matched with N arguments
/// by forming all of the given arguments into a single tuple.
AllowTupleSplatForSingleParameter,
/// Allow a single argument type mismatch. This is the most generic
/// failure related to argument-to-parameter conversions.
AllowArgumentTypeMismatch,
/// Explicitly construct type conforming to `RawRepresentable` protocol
/// via forming `Foo(rawValue:)` instead of using its `RawValue` directly.
ExplicitlyConstructRawRepresentable,
/// Use raw value type associated with raw representable, accessible
/// using `.rawValue` member.
UseRawValue,
/// If an array was passed to a variadic argument, give a specific diagnostic
/// and offer to drop the brackets if it's a literal.
ExpandArrayIntoVarargs,
/// Remove extraneous call to something which can't be invoked e.g.
/// a variable, a property etc.
RemoveCall,
/// Allow an ephemeral argument conversion for a parameter marked as being
/// non-ephemeral.
TreatEphemeralAsNonEphemeral,
/// Base type in reference to the contextual member e.g. `.foo` couldn't be
/// inferred and has to be specified explicitly.
SpecifyBaseTypeForContextualMember,
/// Type of the closure parameter used in the body couldn't be inferred
/// and has to be specified explicitly.
SpecifyClosureParameterType,
/// Closure return type has to be explicitly specified because it can't be
/// inferred in current context e.g. because it's a multi-statement closure.
SpecifyClosureReturnType,
/// Object literal type coudn't be inferred because the module where
/// the default type that implements the associated literal protocol
/// is declared was not imported.
SpecifyObjectLiteralTypeImport,
/// Allow any type (and not just class or class-constrained type) to
/// be convertible to AnyObject.
AllowNonClassTypeToConvertToAnyObject,
/// Member shadows a top-level name, such a name could only be accessed by
/// prefixing it with a module name.
AddQualifierToAccessTopLevelName,
/// A warning fix that allows a coercion to perform a force-cast.
AllowCoercionToForceCast,
/// Allow key path root type mismatch when applying a key path that has a
/// root type not convertible to the type of the base instance.
AllowKeyPathRootTypeMismatch,
/// Allow key path to be bound to a function type with more than 1 argument
AllowMultiArgFuncKeyPathMismatch,
/// Specify key path root type when it cannot be infered from context.
SpecifyKeyPathRootType,
/// Unwrap optional base on key path application.
UnwrapOptionalBaseKeyPathApplication,
/// Explicitly specify a label to match trailing closure to a certain
/// parameter in the call.
SpecifyLabelToAssociateTrailingClosure,
/// Allow key path expressions with no components.
AllowKeyPathWithoutComponents,
/// Ignore result builder body which fails `pre-check` call.
IgnoreInvalidResultBuilderBody,
/// Resolve type of `nil` by providing a contextual type.
SpecifyContextualTypeForNil,
/// Allow expressions to reference invalid declarations by turning
/// them into holes.
AllowRefToInvalidDecl,
/// Treat empty and single-element array literals as if they were incomplete
/// dictionary literals when used as such.
TreatArrayLiteralAsDictionary,
/// Explicitly specify the type to disambiguate between possible member base
/// types.
SpecifyBaseTypeForOptionalUnresolvedMember,
};
class ConstraintFix {
ConstraintSystem &CS;
FixKind Kind;
ConstraintLocator *Locator;
/// Determines whether this fix is simplify a warning which doesn't
/// require immediate source changes.
bool IsWarning;
public:
ConstraintFix(ConstraintSystem &cs, FixKind kind, ConstraintLocator *locator,
bool warning = false)
: CS(cs), Kind(kind), Locator(locator), IsWarning(warning) {}
virtual ~ConstraintFix();
template <typename Fix>
const Fix *getAs() const {
return Fix::classof(this) ? static_cast<const Fix *>(this) : nullptr;
}
FixKind getKind() const { return Kind; }
bool isWarning() const { return IsWarning; }
virtual std::string getName() const = 0;
/// Coalesce this fix with the given secondary fixes and diagnose the failure.
///
/// The default implementation ignores \c secondaryFixes and calls
/// \c diagnose.
virtual bool coalesceAndDiagnose(const Solution &solution,
ArrayRef<ConstraintFix *> secondaryFixes,
bool asNote = false) const {
return diagnose(solution, asNote);
}
/// Diagnose a failure associated with this fix.
virtual bool diagnose(const Solution &solution,
bool asNote = false) const = 0;
using CommonFixesArray =
ArrayRef<std::pair<const Solution *, const ConstraintFix *>>;
virtual bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const {
return false;
}
void print(llvm::raw_ostream &Out) const;
SWIFT_DEBUG_DUMP;
/// Retrieve anchor expression associated with this fix.
/// NOTE: such anchor comes directly from locator without
/// any simplication attempts.
ASTNode getAnchor() const;
ConstraintLocator *getLocator() const { return Locator; }
protected:
ConstraintSystem &getConstraintSystem() const { return CS; }
};
/// Unwrap an optional base when we have a member access.
class UnwrapOptionalBase final : public ConstraintFix {
DeclNameRef MemberName;
Type MemberBaseType;
UnwrapOptionalBase(ConstraintSystem &cs, FixKind kind, DeclNameRef member,
Type memberBaseType, ConstraintLocator *locator)
: ConstraintFix(cs, kind, locator), MemberName(member),
MemberBaseType(memberBaseType) {
assert(kind == FixKind::UnwrapOptionalBase ||
kind == FixKind::UnwrapOptionalBaseWithOptionalResult);
}
public:
std::string getName() const override {
return "unwrap optional base of member lookup";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static UnwrapOptionalBase *create(ConstraintSystem &cs, DeclNameRef member,
Type memberBaseType,
ConstraintLocator *locator);
static UnwrapOptionalBase *
createWithOptionalResult(ConstraintSystem &cs, DeclNameRef member,
Type memberBaseType, ConstraintLocator *locator);
};
// Treat rvalue as if it was an lvalue
class TreatRValueAsLValue final : public ConstraintFix {
TreatRValueAsLValue(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::TreatRValueAsLValue, locator) {}
public:
std::string getName() const override { return "treat rvalue as lvalue"; }
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}
static TreatRValueAsLValue *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
/// Arguments have labeling failures - missing/extraneous or incorrect
/// labels attached to the, fix it by suggesting proper labels.
class RelabelArguments final
: public ConstraintFix,
private llvm::TrailingObjects<RelabelArguments, Identifier> {
friend TrailingObjects;
unsigned NumLabels;
RelabelArguments(ConstraintSystem &cs,
llvm::ArrayRef<Identifier> correctLabels,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::RelabelArguments, locator),
NumLabels(correctLabels.size()) {
std::uninitialized_copy(correctLabels.begin(), correctLabels.end(),
getLabelsBuffer().begin());
}
public:
std::string getName() const override { return "re-label argument(s)"; }
ArrayRef<Identifier> getLabels() const {
return {getTrailingObjects<Identifier>(), NumLabels};
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static RelabelArguments *create(ConstraintSystem &cs,
llvm::ArrayRef<Identifier> correctLabels,
ConstraintLocator *locator);
private:
MutableArrayRef<Identifier> getLabelsBuffer() {
return {getTrailingObjects<Identifier>(), NumLabels};
}
};
/// Add a new conformance to the type to satisfy a requirement.
class MissingConformance final : public ConstraintFix {
// Determines whether given protocol type comes from the context e.g.
// assignment destination or argument comparison.
bool IsContextual;
Type NonConformingType;
// This could either be a protocol or protocol composition.
Type ProtocolType;
MissingConformance(ConstraintSystem &cs, bool isContextual, Type type,
Type protocolType, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AddConformance, locator),
IsContextual(isContextual), NonConformingType(type),
ProtocolType(protocolType) {}
public:
std::string getName() const override {
return "add missing protocol conformance";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static MissingConformance *forRequirement(ConstraintSystem &cs, Type type,
Type protocolType,
ConstraintLocator *locator);
static MissingConformance *forContextual(ConstraintSystem &cs, Type type,
Type protocolType,
ConstraintLocator *locator);
Type getNonConformingType() { return NonConformingType; }
Type getProtocolType() { return ProtocolType; }
};
/// Skip same-type generic requirement constraint,
/// and assume that types are equal.
class SkipSameTypeRequirement final : public ConstraintFix {
Type LHS, RHS;
SkipSameTypeRequirement(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::SkipSameTypeRequirement, locator), LHS(lhs),
RHS(rhs) {}
public:
std::string getName() const override {
return "skip same-type generic requirement";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
Type lhsType() { return LHS; }
Type rhsType() { return RHS; }
static SkipSameTypeRequirement *create(ConstraintSystem &cs, Type lhs,
Type rhs, ConstraintLocator *locator);
};
/// Skip 'superclass' generic requirement constraint,
/// and assume that types are equal.
class SkipSuperclassRequirement final : public ConstraintFix {
Type LHS, RHS;
SkipSuperclassRequirement(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::SkipSuperclassRequirement, locator),
LHS(lhs), RHS(rhs) {}
public:
std::string getName() const override {
return "skip superclass generic requirement";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
Type subclassType() { return LHS; }
Type superclassType() { return RHS; }
static SkipSuperclassRequirement *
create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator);
};
/// For example: Sometimes type returned from the body of the
/// closure doesn't match expected contextual type:
///
/// func foo(_: () -> Int) {}
/// foo { "ultimate question" }
///
/// Body of the closure produces `String` type when `Int` is expected
/// by the context.
class ContextualMismatch : public ConstraintFix {
Type LHS, RHS;
protected:
ContextualMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::ContextualMismatch, locator), LHS(lhs),
RHS(rhs) {}
ContextualMismatch(ConstraintSystem &cs, FixKind kind, Type lhs, Type rhs,
ConstraintLocator *locator, bool warning = false)
: ConstraintFix(cs, kind, locator, warning), LHS(lhs), RHS(rhs) {}
public:
std::string getName() const override { return "fix contextual mismatch"; }
Type getFromType() const { return LHS; }
Type getToType() const { return RHS; }
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override;
static ContextualMismatch *create(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator);
};
class TreatArrayLiteralAsDictionary final : public ContextualMismatch {
TreatArrayLiteralAsDictionary(ConstraintSystem &cs, Type dictionaryTy,
Type arrayTy, ConstraintLocator *locator)
: ContextualMismatch(cs, FixKind::TreatArrayLiteralAsDictionary,
dictionaryTy, arrayTy, locator) {
}
public:
std::string getName() const override {
return "treat array literal as dictionary";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static TreatArrayLiteralAsDictionary *create(ConstraintSystem &cs,
Type dictionaryTy, Type arrayTy,
ConstraintLocator *loc);
};
/// Mark function type as explicitly '@escaping'.
class MarkExplicitlyEscaping final : public ContextualMismatch {
MarkExplicitlyEscaping(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator)
: ContextualMismatch(cs, FixKind::ExplicitlyEscaping, lhs, rhs, locator) {
}
public:
std::string getName() const override { return "add @escaping"; }
bool diagnose(const Solution &solution, bool asNote = false) const override;
static MarkExplicitlyEscaping *create(ConstraintSystem &cs, Type lhs,
Type rhs, ConstraintLocator *locator);
};
/// Introduce a '!' to force an optional unwrap.
class ForceOptional final : public ContextualMismatch {
ForceOptional(ConstraintSystem &cs, Type fromType, Type toType,
ConstraintLocator *locator)
: ContextualMismatch(cs, FixKind::ForceOptional, fromType, toType,
locator) {
assert(fromType && "Base type must not be null");
assert(fromType->getOptionalObjectType() &&
"Unwrapped type must not be null");
}
public:
std::string getName() const override { return "force optional"; }
bool diagnose(const Solution &solution, bool asNote = false) const override;
static ForceOptional *create(ConstraintSystem &cs, Type fromType, Type toType,
ConstraintLocator *locator);
};
/// This is a contextual mismatch between throwing and non-throwing
/// function types, repair it by dropping `throws` attribute.
class DropThrowsAttribute final : public ContextualMismatch {
DropThrowsAttribute(ConstraintSystem &cs, FunctionType *fromType,
FunctionType *toType, ConstraintLocator *locator)
: ContextualMismatch(cs, fromType, toType, locator) {
assert(fromType->isThrowing() != toType->isThrowing());
}
public:
std::string getName() const override { return "drop 'throws' attribute"; }
bool diagnose(const Solution &solution, bool asNote = false) const override;
static DropThrowsAttribute *create(ConstraintSystem &cs,
FunctionType *fromType,
FunctionType *toType,
ConstraintLocator *locator);
};
/// This is a contextual mismatch between async and non-async
/// function types, repair it by dropping `async` attribute.
class DropAsyncAttribute final : public ContextualMismatch {
DropAsyncAttribute(ConstraintSystem &cs, FunctionType *fromType,
FunctionType *toType, ConstraintLocator *locator)
: ContextualMismatch(cs, fromType, toType, locator) {
assert(fromType->isAsync() != toType->isAsync());
}
public:
std::string getName() const override { return "drop 'async' attribute"; }
bool diagnose(const Solution &solution, bool asNote = false) const override;
static DropAsyncAttribute *create(ConstraintSystem &cs,
FunctionType *fromType,
FunctionType *toType,
ConstraintLocator *locator);
};
/// Append 'as! T' to force a downcast to the specified type.
class ForceDowncast final : public ContextualMismatch {
ForceDowncast(ConstraintSystem &cs, Type fromType, Type toType,
ConstraintLocator *locator)
: ContextualMismatch(cs, FixKind::ForceDowncast, fromType, toType,
locator) {}
public:
std::string getName() const override;
bool diagnose(const Solution &solution, bool asNote = false) const override;
static ForceDowncast *create(ConstraintSystem &cs, Type fromType, Type toType,
ConstraintLocator *locator);
};
/// Introduce a '&' to take the address of an lvalue.
class AddAddressOf final : public ContextualMismatch {
AddAddressOf(ConstraintSystem &cs, Type argTy, Type paramTy,
ConstraintLocator *locator)
: ContextualMismatch(cs, FixKind::AddressOf, argTy, paramTy, locator) {}
public:
std::string getName() const override { return "add address-of"; }
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AddAddressOf *create(ConstraintSystem &cs, Type argTy, Type paramTy,
ConstraintLocator *locator);
};
class RemoveAddressOf final : public ContextualMismatch {
RemoveAddressOf(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator)
: ContextualMismatch(cs, FixKind::RemoveAddressOf, lhs, rhs, locator) {}
public:
std::string getName() const override {
return "remove extraneous use of `&`";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static RemoveAddressOf *create(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator);
};
/// Detect situations where two type's generic arguments must
/// match but are not convertible e.g.
///
/// ```swift
/// struct F<G> {}
/// let _:F<Int> = F<Bool>()
/// ```
class GenericArgumentsMismatch final
: public ContextualMismatch,
private llvm::TrailingObjects<GenericArgumentsMismatch, unsigned> {
friend TrailingObjects;
unsigned NumMismatches;
protected:
GenericArgumentsMismatch(ConstraintSystem &cs, Type actual, Type required,
llvm::ArrayRef<unsigned> mismatches,
ConstraintLocator *locator)
: ContextualMismatch(cs, FixKind::GenericArgumentsMismatch, actual,
required, locator),
NumMismatches(mismatches.size()) {
assert(actual->is<BoundGenericType>());
assert(required->is<BoundGenericType>());
std::uninitialized_copy(mismatches.begin(), mismatches.end(),
getMismatchesBuf().begin());
}
public:
std::string getName() const override {
return "fix generic argument mismatch";
}
ArrayRef<unsigned> getMismatches() const {
return {getTrailingObjects<unsigned>(), NumMismatches};
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static GenericArgumentsMismatch *create(ConstraintSystem &cs, Type actual,
Type required,
llvm::ArrayRef<unsigned> mismatches,
ConstraintLocator *locator);
private:
MutableArrayRef<unsigned> getMismatchesBuf() {
return {getTrailingObjects<unsigned>(), NumMismatches};
}
};
/// Detect situations where key path doesn't have capability required
/// by the context e.g. read-only vs. writable, or either root or value
/// types are incorrect e.g.
///
/// ```swift
/// struct S { let foo: Int }
/// let _: WritableKeyPath<S, Int> = \.foo
/// ```
///
/// Here context requires a writable key path but `foo` property is
/// read-only.
class KeyPathContextualMismatch final : public ContextualMismatch {
KeyPathContextualMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator)
: ContextualMismatch(cs, lhs, rhs, locator) {}
public:
std::string getName() const override {
return "fix key path contextual mismatch";
}
static KeyPathContextualMismatch *
create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator);
};
/// Detect situations when argument of the @autoclosure parameter is itself
/// marked as @autoclosure and is not applied. Form a fix which suggests a
/// proper way to forward such arguments, e.g.:
///
/// ```swift
/// func foo(_ fn: @autoclosure () -> Int) {}
/// func bar(_ fn: @autoclosure () -> Int) {
/// foo(fn) // error - fn should be called
/// }
/// ```
class AutoClosureForwarding final : public ConstraintFix {
AutoClosureForwarding(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AutoClosureForwarding, locator) {}
public:
std::string getName() const override { return "fix @autoclosure forwarding"; }
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AutoClosureForwarding *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
/// Allow invalid pointer conversions for autoclosure result types as if the
/// pointer type is a function parameter rather than an autoclosure result.
class AllowAutoClosurePointerConversion final : public ContextualMismatch {
AllowAutoClosurePointerConversion(ConstraintSystem &cs, Type pointeeType,
Type pointerType, ConstraintLocator *locator)
: ContextualMismatch(cs, pointeeType, pointerType, locator) {}
public:
std::string getName() const override {
return "allow pointer conversion for autoclosure result type";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowAutoClosurePointerConversion *create(ConstraintSystem &cs,
Type pointeeType,
Type pointerType,
ConstraintLocator *locator);
};
class RemoveUnwrap final : public ConstraintFix {
Type BaseType;
RemoveUnwrap(ConstraintSystem &cs, Type baseType, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::RemoveUnwrap, locator), BaseType(baseType) {}
public:
std::string getName() const override {
return "remove unwrap operator `!` or `?`";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static RemoveUnwrap *create(ConstraintSystem &cs, Type baseType,
ConstraintLocator *locator);
};
class InsertExplicitCall final : public ConstraintFix {
InsertExplicitCall(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::InsertCall, locator) {}
public:
std::string getName() const override {
return "insert explicit `()` to make a call";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static InsertExplicitCall *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
class UsePropertyWrapper final : public ConstraintFix {
VarDecl *Wrapped;
bool UsingProjection;
Type Base;
Type Wrapper;
UsePropertyWrapper(ConstraintSystem &cs, VarDecl *wrapped,
bool usingProjection, Type base, Type wrapper,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::UsePropertyWrapper, locator),
Wrapped(wrapped), UsingProjection(usingProjection), Base(base),
Wrapper(wrapper) {}
public:
std::string getName() const override {
return "insert '$' or '_' to use property wrapper type instead of wrapped type";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}
static UsePropertyWrapper *create(ConstraintSystem &cs, VarDecl *wrapped,
bool usingProjection, Type base,
Type wrapper, ConstraintLocator *locator);
};
class UseWrappedValue final : public ConstraintFix {
VarDecl *PropertyWrapper;
Type Base;
Type Wrapper;
UseWrappedValue(ConstraintSystem &cs, VarDecl *propertyWrapper, Type base,
Type wrapper, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::UseWrappedValue, locator),
PropertyWrapper(propertyWrapper), Base(base), Wrapper(wrapper) {}
bool usingProjection() const {
auto nameStr = PropertyWrapper->getName().str();
return !nameStr.startswith("_");
}
public:
std::string getName() const override {
return "remove '$' or _ to use wrapped type instead of wrapper type";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}
static UseWrappedValue *create(ConstraintSystem &cs, VarDecl *propertyWrapper,
Type base, Type wrapper,
ConstraintLocator *locator);
};
class UseSubscriptOperator final : public ConstraintFix {
UseSubscriptOperator(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::UseSubscriptOperator, locator) {}
public:
std::string getName() const override {
return "replace '.subscript(...)' with subscript operator";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static UseSubscriptOperator *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
class DefineMemberBasedOnUse final : public ConstraintFix {
Type BaseType;
DeclNameRef Name;
/// Whether or not the member error is already diagnosed. This can happen
/// when referencing an erroneous member, and the error is diagnosed at the
/// member declaration.
///
/// We still want to define erroneous members based on use in order to find
/// a solution through the new diagnostic infrastructure, but we don't
/// want to report a second error message.
bool AlreadyDiagnosed;
DefineMemberBasedOnUse(ConstraintSystem &cs, Type baseType, DeclNameRef member,
bool alreadyDiagnosed, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::DefineMemberBasedOnUse, locator),
BaseType(baseType), Name(member), AlreadyDiagnosed(alreadyDiagnosed) {}
public:
std::string getName() const override {
llvm::SmallVector<char, 16> scratch;
auto memberName = Name.getString(scratch);
return "define missing member named '" + memberName.str() +
"' based on its use";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override;
static DefineMemberBasedOnUse *create(ConstraintSystem &cs, Type baseType,
DeclNameRef member, bool alreadyDiagnosed,
ConstraintLocator *locator);
static bool classof(const ConstraintFix *fix) {
return fix->getKind() == FixKind::DefineMemberBasedOnUse;
}
};
class DefineMemberBasedOnUnintendedGenericParam final : public ConstraintFix {
Type BaseType;
DeclNameRef Name;
Identifier ParamName;
DefineMemberBasedOnUnintendedGenericParam(ConstraintSystem &cs, Type baseType,
DeclNameRef member,
Identifier paramName,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::DefineMemberBasedOnUse, locator),
BaseType(baseType), Name(member), ParamName(paramName) {}
public:
std::string getName() const override {
llvm::SmallVector<char, 16> scratch;
auto memberName = Name.getString(scratch);
return "allow access to invalid member '" + memberName.str() +
"' on archetype presumed intended to conform to protocol";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static DefineMemberBasedOnUnintendedGenericParam *
create(ConstraintSystem &cs, Type baseType, DeclNameRef member,
Identifier paramName, ConstraintLocator *locator);
};
class AllowInvalidMemberRef : public ConstraintFix {
Type BaseType;
ValueDecl *Member;
DeclNameRef Name;
protected:
AllowInvalidMemberRef(ConstraintSystem &cs, FixKind kind, Type baseType,
ValueDecl *member, DeclNameRef name,
ConstraintLocator *locator)
: ConstraintFix(cs, kind, locator), BaseType(baseType), Member(member),
Name(name) {}
public:
Type getBaseType() const { return BaseType; }
ValueDecl *getMember() const { return Member; }
DeclNameRef getMemberName() const { return Name; }
};
class AllowMemberRefOnExistential final : public AllowInvalidMemberRef {
AllowMemberRefOnExistential(ConstraintSystem &cs, Type baseType,
DeclNameRef memberName, ValueDecl *member,
ConstraintLocator *locator)
: AllowInvalidMemberRef(cs, FixKind::AllowMemberRefOnExistential,
baseType, member, memberName, locator) {}
public:
std::string getName() const override {
llvm::SmallVector<char, 16> scratch;
auto memberName = getMemberName().getString(scratch);
return "allow access to invalid member '" + memberName.str() +
"' on value of protocol type";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowMemberRefOnExistential *create(ConstraintSystem &cs,
Type baseType, ValueDecl *member,
DeclNameRef memberName,
ConstraintLocator *locator);
};
class AllowTypeOrInstanceMember final : public AllowInvalidMemberRef {
AllowTypeOrInstanceMember(ConstraintSystem &cs, Type baseType,
ValueDecl *member, DeclNameRef name,
ConstraintLocator *locator)
: AllowInvalidMemberRef(cs, FixKind::AllowTypeOrInstanceMember, baseType,
member, name, locator) {
assert(member);
}
public:
std::string getName() const override {
return "allow access to instance member on type or a type member on instance";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowTypeOrInstanceMember *create(ConstraintSystem &cs, Type baseType,
ValueDecl *member, DeclNameRef usedName,
ConstraintLocator *locator);
};
class AllowInvalidPartialApplication final : public ConstraintFix {
AllowInvalidPartialApplication(bool isWarning, ConstraintSystem &cs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowInvalidPartialApplication, locator,
isWarning) {}
public:
std::string getName() const override {
return "allow partially applied 'mutating' method";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowInvalidPartialApplication *create(bool isWarning,
ConstraintSystem &cs,
ConstraintLocator *locator);
};
class AllowInvalidInitRef final : public ConstraintFix {
enum class RefKind {
DynamicOnMetatype,
ProtocolMetatype,
NonConstMetatype,
} Kind;
Type BaseType;
const ConstructorDecl *Init;
bool IsStaticallyDerived;
SourceRange BaseRange;
AllowInvalidInitRef(ConstraintSystem &cs, RefKind kind, Type baseTy,
ConstructorDecl *init, bool isStaticallyDerived,
SourceRange baseRange, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowInvalidInitRef, locator), Kind(kind),
BaseType(baseTy), Init(init), IsStaticallyDerived(isStaticallyDerived),
BaseRange(baseRange) {}
public:
std::string getName() const override {
return "allow invalid initializer reference";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowInvalidInitRef *
dynamicOnMetatype(ConstraintSystem &cs, Type baseTy, ConstructorDecl *init,
SourceRange baseRange, ConstraintLocator *locator);
static AllowInvalidInitRef *
onProtocolMetatype(ConstraintSystem &cs, Type baseTy, ConstructorDecl *init,
bool isStaticallyDerived, SourceRange baseRange,
ConstraintLocator *locator);
static AllowInvalidInitRef *onNonConstMetatype(ConstraintSystem &cs,
Type baseTy,
ConstructorDecl *init,
ConstraintLocator *locator);
private:
static AllowInvalidInitRef *create(RefKind kind, ConstraintSystem &cs,
Type baseTy, ConstructorDecl *init,
bool isStaticallyDerived,
SourceRange baseRange,
ConstraintLocator *locator);
};
class AllowTupleTypeMismatch final : public ContextualMismatch {
/// If this is an element mismatch, \c Index is the element index where the
/// type mismatch occurred. If this is an arity or label mismatch, \c Index
/// will be \c None.
Optional<unsigned> Index;
AllowTupleTypeMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator, Optional<unsigned> index)
: ContextualMismatch(cs, FixKind::AllowTupleTypeMismatch, lhs, rhs,
locator), Index(index) {}
public:
static AllowTupleTypeMismatch *create(ConstraintSystem &cs, Type lhs,
Type rhs, ConstraintLocator *locator,
Optional<unsigned> index = None);
static bool classof(const ConstraintFix *fix) {
return fix->getKind() == FixKind::AllowTupleTypeMismatch;
}
std::string getName() const override {
return "fix tuple mismatches in type and arity";
}
bool isElementMismatch() const {
return Index.hasValue();
}
bool coalesceAndDiagnose(const Solution &solution,
ArrayRef<ConstraintFix *> secondaryFixes,
bool asNote = false) const override;
bool diagnose(const Solution &solution, bool asNote = false) const override;
};
class AllowFunctionTypeMismatch final : public ContextualMismatch {
/// The index of the parameter where the type mismatch occurred.
unsigned ParamIndex;
AllowFunctionTypeMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator, unsigned index)
: ContextualMismatch(cs, FixKind::AllowFunctionTypeMismatch, lhs, rhs,
locator), ParamIndex(index) {}
public:
static AllowFunctionTypeMismatch *create(ConstraintSystem &cs, Type lhs,
Type rhs, ConstraintLocator *locator,
unsigned index);
static bool classof(const ConstraintFix *fix) {
return fix->getKind() == FixKind::AllowFunctionTypeMismatch;
}
std::string getName() const override {
return "allow function type mismatch";
}
bool coalesceAndDiagnose(const Solution &solution,
ArrayRef<ConstraintFix *> secondaryFixes,
bool asNote = false) const override;
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override;
};
class AllowMutatingMemberOnRValueBase final : public AllowInvalidMemberRef {
AllowMutatingMemberOnRValueBase(ConstraintSystem &cs, Type baseType,
ValueDecl *member, DeclNameRef name,
ConstraintLocator *locator)
: AllowInvalidMemberRef(cs, FixKind::AllowMutatingMemberOnRValueBase,
baseType, member, name, locator) {}
public:
std::string getName() const override {
return "allow `mutating` method on r-value base";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowMutatingMemberOnRValueBase *
create(ConstraintSystem &cs, Type baseType, ValueDecl *member,
DeclNameRef name, ConstraintLocator *locator);
};
class AllowClosureParamDestructuring final : public ConstraintFix {
FunctionType *ContextualType;
AllowClosureParamDestructuring(ConstraintSystem &cs,
FunctionType *contextualType,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowClosureParameterDestructuring, locator),
ContextualType(contextualType) {}
public:
std::string getName() const override {
return "allow closure parameter destructuring";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowClosureParamDestructuring *create(ConstraintSystem &cs,
FunctionType *contextualType,
ConstraintLocator *locator);
};
struct SynthesizedArg {
unsigned paramIdx;
AnyFunctionType::Param param;
};
class AddMissingArguments final
: public ConstraintFix,
private llvm::TrailingObjects<
AddMissingArguments, SynthesizedArg> {
friend TrailingObjects;
unsigned NumSynthesized;
AddMissingArguments(ConstraintSystem &cs,
ArrayRef<SynthesizedArg> synthesizedArgs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AddMissingArguments, locator),
NumSynthesized(synthesizedArgs.size()) {
std::uninitialized_copy(synthesizedArgs.begin(), synthesizedArgs.end(),
getSynthesizedArgumentsBuf().begin());
}
public:
std::string getName() const override { return "synthesize missing argument(s)"; }
ArrayRef<SynthesizedArg> getSynthesizedArguments() const {
return {getTrailingObjects<SynthesizedArg>(), NumSynthesized};
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}
static AddMissingArguments *create(ConstraintSystem &cs,
ArrayRef<SynthesizedArg> synthesizedArgs,
ConstraintLocator *locator);
static bool classof(const ConstraintFix *fix) {
return fix->getKind() == FixKind::AddMissingArguments;
}
private:
MutableArrayRef<SynthesizedArg> getSynthesizedArgumentsBuf() {
return {getTrailingObjects<SynthesizedArg>(), NumSynthesized};
}
};
class RemoveExtraneousArguments final
: public ConstraintFix,
private llvm::TrailingObjects<
RemoveExtraneousArguments,
std::pair<unsigned, AnyFunctionType::Param>> {
friend TrailingObjects;
using IndexedParam = std::pair<unsigned, AnyFunctionType::Param>;
FunctionType *ContextualType;
unsigned NumExtraneous;
RemoveExtraneousArguments(ConstraintSystem &cs, FunctionType *contextualType,
llvm::ArrayRef<IndexedParam> extraArgs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::RemoveExtraneousArguments, locator),
ContextualType(contextualType), NumExtraneous(extraArgs.size()) {
std::uninitialized_copy(extraArgs.begin(), extraArgs.end(),
getExtraArgumentsBuf().begin());
}
public:
std::string getName() const override { return "remove extraneous argument(s)"; }
ArrayRef<IndexedParam> getExtraArguments() const {
return {getTrailingObjects<IndexedParam>(), NumExtraneous};
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}
/// FIXME(diagnostics): Once `resolveDeclRefExpr` is gone this
/// logic would be obsolete.
///
/// Determine whether presence of extraneous arguments indicates
/// potential name shadowing problem with local `min`/`max` shadowing
/// global definitions with different number of arguments.
static bool isMinMaxNameShadowing(ConstraintSystem &cs,
ConstraintLocatorBuilder locator);
static RemoveExtraneousArguments *
create(ConstraintSystem &cs, FunctionType *contextualType,
llvm::ArrayRef<IndexedParam> extraArgs, ConstraintLocator *locator);
private:
MutableArrayRef<IndexedParam> getExtraArgumentsBuf() {
return {getTrailingObjects<IndexedParam>(), NumExtraneous};
}
};
class MoveOutOfOrderArgument final : public ConstraintFix {
using ParamBinding = SmallVector<unsigned, 1>;
unsigned ArgIdx;
unsigned PrevArgIdx;
SmallVector<ParamBinding, 4> Bindings;
MoveOutOfOrderArgument(ConstraintSystem &cs, unsigned argIdx,
unsigned prevArgIdx, ArrayRef<ParamBinding> bindings,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::MoveOutOfOrderArgument, locator),
ArgIdx(argIdx), PrevArgIdx(prevArgIdx),
Bindings(bindings.begin(), bindings.end()) {}
public:
std::string getName() const override {
return "move out-of-order argument to correct position";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static MoveOutOfOrderArgument *create(ConstraintSystem &cs,
unsigned argIdx,
unsigned prevArgIdx,
ArrayRef<ParamBinding> bindings,
ConstraintLocator *locator);
};
class AllowInaccessibleMember final : public AllowInvalidMemberRef {
AllowInaccessibleMember(ConstraintSystem &cs, Type baseType,
ValueDecl *member, DeclNameRef name,
ConstraintLocator *locator)
: AllowInvalidMemberRef(cs, FixKind::AllowInaccessibleMember, baseType,
member, name, locator) {}
public:
std::string getName() const override {
return "allow inaccessible member reference";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowInaccessibleMember *create(ConstraintSystem &cs, Type baseType,
ValueDecl *member, DeclNameRef name,
ConstraintLocator *locator);
};
class AllowAnyObjectKeyPathRoot final : public ConstraintFix {
AllowAnyObjectKeyPathRoot(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowAnyObjectKeyPathRoot, locator) {}
public:
std::string getName() const override {
return "allow anyobject as root type for a keypath";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowAnyObjectKeyPathRoot *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
class AllowMultiArgFuncKeyPathMismatch final : public ConstraintFix {
Type functionType;
AllowMultiArgFuncKeyPathMismatch(ConstraintSystem &cs, Type fnType,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowMultiArgFuncKeyPathMismatch, locator),
functionType(fnType) {}
public:
std::string getName() const override {
return "allow conversion of a keypath type to a multi-argument function";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowMultiArgFuncKeyPathMismatch *create(ConstraintSystem &cs,
Type fnType,
ConstraintLocator *locator);
};
class TreatKeyPathSubscriptIndexAsHashable final : public ConstraintFix {
Type NonConformingType;
TreatKeyPathSubscriptIndexAsHashable(ConstraintSystem &cs, Type type,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::TreatKeyPathSubscriptIndexAsHashable,
locator),
NonConformingType(type) {}
public:
std::string getName() const override {
return "treat keypath subscript index as conforming to Hashable";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static TreatKeyPathSubscriptIndexAsHashable *
create(ConstraintSystem &cs, Type type, ConstraintLocator *locator);
};
class AllowInvalidRefInKeyPath final : public ConstraintFix {
enum RefKind {
// Allow a reference to a static member as a key path component.
StaticMember,
// Allow a reference to a declaration with mutating getter as
// a key path component.
MutatingGetter,
// Allow a reference to a method (instance or static) as
// a key path component.
Method,
// Allow a reference to a initializer instance as a key path
// component.
Initializer,
// Allow a reference to an enum case as a key path component.
EnumCase,
} Kind;
ValueDecl *Member;
AllowInvalidRefInKeyPath(ConstraintSystem &cs, RefKind kind,
ValueDecl *member, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowInvalidRefInKeyPath, locator),
Kind(kind), Member(member) {}
public:
std::string getName() const override {
switch (Kind) {
case RefKind::StaticMember:
return "allow reference to a static member as a key path component";
case RefKind::MutatingGetter:
return "allow reference to a member with mutating getter as a key "
"path component";
case RefKind::Method:
return "allow reference to a method as a key path component";
case RefKind::Initializer:
return "allow reference to an init method as a key path component";
case RefKind::EnumCase:
return "allow reference to an enum case as a key path component";
}
llvm_unreachable("covered switch");
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
/// Determine whether give reference requires a fix and produce one.
static AllowInvalidRefInKeyPath *
forRef(ConstraintSystem &cs, ValueDecl *member, ConstraintLocator *locator);
private:
static AllowInvalidRefInKeyPath *create(ConstraintSystem &cs, RefKind kind,
ValueDecl *member,
ConstraintLocator *locator);
};
class RemoveReturn final : public ContextualMismatch {
RemoveReturn(ConstraintSystem &cs, Type resultTy, ConstraintLocator *locator);
public:
std::string getName() const override { return "remove or omit return type"; }
bool diagnose(const Solution &solution, bool asNote = false) const override;
static RemoveReturn *create(ConstraintSystem &cs, Type resultTy,
ConstraintLocator *locator);
};
class CollectionElementContextualMismatch final : public ContextualMismatch {
CollectionElementContextualMismatch(ConstraintSystem &cs, Type srcType,
Type dstType, ConstraintLocator *locator)
: ContextualMismatch(cs, srcType, dstType, locator) {}
public:
std::string getName() const override {
return "fix collection element contextual mismatch";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static CollectionElementContextualMismatch *
create(ConstraintSystem &cs, Type srcType, Type dstType,
ConstraintLocator *locator);
};
class DefaultGenericArgument final : public ConstraintFix {
GenericTypeParamType *Param;
DefaultGenericArgument(ConstraintSystem &cs, GenericTypeParamType *param,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::DefaultGenericArgument, locator),
Param(param) {}
public:
static bool classof(const ConstraintFix *fix) {
return fix->getKind() == FixKind::DefaultGenericArgument;
}
std::string getName() const override {
auto paramName = Param->getString();
return "default generic argument '" + paramName + "' to 'Any'";
}
bool coalesceAndDiagnose(const Solution &solution,
ArrayRef<ConstraintFix *> secondaryFixes,
bool asNote = false) const override;
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}
static DefaultGenericArgument *create(ConstraintSystem &cs,
GenericTypeParamType *param,
ConstraintLocator *locator);
};
class SkipUnhandledConstructInResultBuilder final : public ConstraintFix {
public:
using UnhandledNode = llvm::PointerUnion<Stmt *, Decl *>;
private:
UnhandledNode unhandled;
NominalTypeDecl *builder;
SkipUnhandledConstructInResultBuilder(ConstraintSystem &cs,
UnhandledNode unhandled,
NominalTypeDecl *builder,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::SkipUnhandledConstructInResultBuilder,
locator),
unhandled(unhandled), builder(builder) { }
public:
std::string getName() const override {
return "skip unhandled constructs when applying a result builder";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static SkipUnhandledConstructInResultBuilder *
create(ConstraintSystem &cs, UnhandledNode unhandledNode,
NominalTypeDecl *builder, ConstraintLocator *locator);
};
class AllowTupleSplatForSingleParameter final : public ConstraintFix {
using Param = AnyFunctionType::Param;
Type ParamType;
AllowTupleSplatForSingleParameter(ConstraintSystem &cs, Type paramTy,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowTupleSplatForSingleParameter, locator),
ParamType(paramTy) {}
public:
std::string getName() const override {
return "allow single parameter tuple splat";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
/// Apply this fix to given arguments/parameters and return `true`
/// this fix is not applicable and solver can't continue, `false`
/// otherwise.
static bool attempt(ConstraintSystem &cs, SmallVectorImpl<Param> &args,
ArrayRef<Param> params,
SmallVectorImpl<SmallVector<unsigned, 1>> &bindings,
ConstraintLocatorBuilder locator);
};
class IgnoreContextualType : public ContextualMismatch {
IgnoreContextualType(ConstraintSystem &cs, Type resultTy, Type specifiedTy,
ConstraintLocator *locator)
: ContextualMismatch(cs, resultTy, specifiedTy, locator) {}
public:
std::string getName() const override {
return "ignore specified contextual type";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static IgnoreContextualType *create(ConstraintSystem &cs, Type resultTy,
Type specifiedTy,
ConstraintLocator *locator);
};
class IgnoreAssignmentDestinationType final : public ContextualMismatch {
IgnoreAssignmentDestinationType(ConstraintSystem &cs, Type sourceTy,
Type destTy, ConstraintLocator *locator)
: ContextualMismatch(cs, sourceTy, destTy, locator) {}
public:
std::string getName() const override {
return "ignore type of the assignment destination";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override;
static IgnoreAssignmentDestinationType *create(ConstraintSystem &cs,
Type sourceTy, Type destTy,
ConstraintLocator *locator);
};
/// If this is an argument-to-parameter conversion which is associated with
/// `inout` parameter, subtyping is not permitted, types have to
/// be identical.
class AllowInOutConversion final : public ContextualMismatch {
AllowInOutConversion(ConstraintSystem &cs, Type argType, Type paramType,
ConstraintLocator *locator)
: ContextualMismatch(cs, argType, paramType, locator) {}
public:
std::string getName() const override {
return "allow conversions between argument/parameter marked as `inout`";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowInOutConversion *create(ConstraintSystem &cs, Type argType,
Type paramType,
ConstraintLocator *locator);
};
class AllowArgumentMismatch : public ContextualMismatch {
protected:
AllowArgumentMismatch(ConstraintSystem &cs, Type argType, Type paramType,
ConstraintLocator *locator)
: AllowArgumentMismatch(cs, FixKind::AllowArgumentTypeMismatch, argType,
paramType, locator) {}
AllowArgumentMismatch(ConstraintSystem &cs, FixKind kind, Type argType,
Type paramType, ConstraintLocator *locator,
bool warning = false)
: ContextualMismatch(cs, kind, argType, paramType, locator, warning) {}
public:
std::string getName() const override {
return "allow argument to parameter type conversion mismatch";
}
unsigned getParamIdx() const;
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowArgumentMismatch *create(ConstraintSystem &cs, Type argType,
Type paramType,
ConstraintLocator *locator);
static bool classof(const ConstraintFix *fix) {
return fix->getKind() == FixKind::AllowArgumentTypeMismatch;
}
};
class ExpandArrayIntoVarargs final : public AllowArgumentMismatch {
ExpandArrayIntoVarargs(ConstraintSystem &cs, Type argType, Type paramType,
ConstraintLocator *locator)
: AllowArgumentMismatch(cs, FixKind::ExpandArrayIntoVarargs, argType,
paramType, locator) {}
public:
std::string getName() const override {
return "cannot pass Array elements as variadic arguments";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static ExpandArrayIntoVarargs *attempt(ConstraintSystem &cs, Type argType,
Type paramType,
ConstraintLocatorBuilder locator);
};
class ExplicitlyConstructRawRepresentable final : public ConstraintFix {
Type RawReprType;
Type ExpectedType;
ExplicitlyConstructRawRepresentable(ConstraintSystem &cs, Type rawReprType,
Type expectedType,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::ExplicitlyConstructRawRepresentable,
locator),
RawReprType(rawReprType), ExpectedType(expectedType) {}
public:
std::string getName() const override {
return "explicitly construct a raw representable type";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static ExplicitlyConstructRawRepresentable *
create(ConstraintSystem &cs, Type rawTypeRepr, Type expectedType,
ConstraintLocator *locator);
};
class UseRawValue final : public ConstraintFix {
Type RawReprType;
Type ExpectedType;
UseRawValue(ConstraintSystem &cs, Type rawReprType, Type expectedType,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::UseRawValue, locator),
RawReprType(rawReprType), ExpectedType(expectedType) {}
public:
std::string getName() const override {
return "use `.rawValue` of a raw representable type";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static UseRawValue *create(ConstraintSystem &cs, Type rawReprType,
Type expectedType, ConstraintLocator *locator);
};
/// Replace a coercion ('as') with runtime checked cast ('as!' or 'as?').
class CoerceToCheckedCast final : public ContextualMismatch {
CoerceToCheckedCast(ConstraintSystem &cs, Type fromType, Type toType,
bool useConditionalCast, ConstraintLocator *locator)
: ContextualMismatch(cs, FixKind::CoerceToCheckedCast, fromType, toType,
locator),
UseConditionalCast(useConditionalCast) {}
bool UseConditionalCast = false;
public:
std::string getName() const override {
return UseConditionalCast ? "as to as?" : "as to as!";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static CoerceToCheckedCast *attempt(ConstraintSystem &cs, Type fromType,
Type toType, bool useConditionalCast,
ConstraintLocator *locator);
};
class RemoveInvalidCall final : public ConstraintFix {
RemoveInvalidCall(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::RemoveCall, locator) {}
public:
std::string getName() const override {
return "remove extraneous call from value of non-function type";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static RemoveInvalidCall *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
class TreatEphemeralAsNonEphemeral final : public AllowArgumentMismatch {
ConversionRestrictionKind ConversionKind;
TreatEphemeralAsNonEphemeral(ConstraintSystem &cs, ConstraintLocator *locator,
Type srcType, Type dstType,
ConversionRestrictionKind conversionKind,
bool downgradeToWarning)
: AllowArgumentMismatch(cs, FixKind::TreatEphemeralAsNonEphemeral,
srcType, dstType, locator, downgradeToWarning),
ConversionKind(conversionKind) {}
public:
ConversionRestrictionKind getConversionKind() const { return ConversionKind; }
std::string getName() const override;
bool diagnose(const Solution &solution, bool asNote = false) const override;
static TreatEphemeralAsNonEphemeral *
create(ConstraintSystem &cs, ConstraintLocator *locator, Type srcType,
Type dstType, ConversionRestrictionKind conversionKind,
bool downgradeToWarning);
};
class SpecifyBaseTypeForContextualMember final : public ConstraintFix {
DeclNameRef MemberName;
SpecifyBaseTypeForContextualMember(ConstraintSystem &cs, DeclNameRef member,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::SpecifyBaseTypeForContextualMember, locator),
MemberName(member) {}
public:
std::string getName() const override {
const auto baseName = MemberName.getBaseName();
return "specify base type in reference to member '" +
baseName.userFacingName().str() + "'";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}
static SpecifyBaseTypeForContextualMember *
create(ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator);
};
class SpecifyClosureParameterType final : public ConstraintFix {
SpecifyClosureParameterType(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::SpecifyClosureParameterType, locator) {}
public:
std::string getName() const override;
bool diagnose(const Solution &solution, bool asNote = false) const override;
static SpecifyClosureParameterType *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
class SpecifyClosureReturnType final : public ConstraintFix {
SpecifyClosureReturnType(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::SpecifyClosureReturnType, locator) {}
public:
std::string getName() const override {
return "specify closure return type";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static SpecifyClosureReturnType *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
class SpecifyObjectLiteralTypeImport final : public ConstraintFix {
SpecifyObjectLiteralTypeImport(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::SpecifyObjectLiteralTypeImport, locator) {}
public:
std::string getName() const override {
return "import required module to gain access to a default literal type";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static SpecifyObjectLiteralTypeImport *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
class AddQualifierToAccessTopLevelName final : public ConstraintFix {
AddQualifierToAccessTopLevelName(ConstraintSystem &cs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AddQualifierToAccessTopLevelName, locator) {}
public:
std::string getName() const override {
return "qualify reference to access top-level function";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AddQualifierToAccessTopLevelName *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
class AllowNonClassTypeToConvertToAnyObject final : public ContextualMismatch {
AllowNonClassTypeToConvertToAnyObject(ConstraintSystem &cs, Type type,
ConstraintLocator *locator);
public:
std::string getName() const override {
return "allow non-class type to convert to 'AnyObject'";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowNonClassTypeToConvertToAnyObject *
create(ConstraintSystem &cs, Type type, ConstraintLocator *locator);
};
/// A warning fix to maintain compatibility with the following:
///
/// \code
/// func foo(_ arr: [Any]?) {
/// _ = (arr ?? []) as [NSObject]
/// }
/// \endcode
///
/// which performs a force-cast of the array's elements, despite being spelled
/// as a coercion.
class AllowCoercionToForceCast final : public ContextualMismatch {
AllowCoercionToForceCast(ConstraintSystem &cs, Type fromType, Type toType,
ConstraintLocator *locator)
: ContextualMismatch(cs, FixKind::AllowCoercionToForceCast, fromType,
toType, locator, /*warning*/ true) {}
public:
std::string getName() const override {
return "allow coercion to be treated as a force-cast";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowCoercionToForceCast *create(ConstraintSystem &cs, Type fromType,
Type toType,
ConstraintLocator *locator);
};
/// Attempt to fix a key path application where the key path type cannot be
/// applied to a base instance of another type.
///
/// \code
/// func f(_ bar: Bar , keyPath: KeyPath<Foo, Int> ) {
/// bar[keyPath: keyPath]
/// }
/// \endcode
class AllowKeyPathRootTypeMismatch final : public ContextualMismatch {
protected:
AllowKeyPathRootTypeMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator)
: ContextualMismatch(cs, FixKind::AllowKeyPathRootTypeMismatch, lhs, rhs,
locator) {}
public:
std::string getName() const override {
return "allow key path root type mismatch";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowKeyPathRootTypeMismatch *
create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator);
};
class SpecifyKeyPathRootType final : public ConstraintFix {
SpecifyKeyPathRootType(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::SpecifyKeyPathRootType, locator) {}
public:
std::string getName() const override {
return "specify key path root type";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static SpecifyKeyPathRootType *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
/// Diagnose missing unwrap of optional base type on key path application.
///
/// \code
/// func f(_ bar: Bar? , keyPath: KeyPath<Bar, Int>) {
/// bar[keyPath: keyPath]
/// }
/// \endcode
class UnwrapOptionalBaseKeyPathApplication final : public ContextualMismatch {
protected:
UnwrapOptionalBaseKeyPathApplication(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator)
: ContextualMismatch(cs, FixKind::UnwrapOptionalBaseKeyPathApplication,
lhs, rhs, locator) {}
public:
std::string getName() const override {
return "force unwrap base on key path application";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static UnwrapOptionalBaseKeyPathApplication *
attempt(ConstraintSystem &cs, Type baseTy, Type rootTy,
ConstraintLocator *locator);
};
/// Diagnose situations when solver used old (backward scan) rule
/// to match trailing closure to a parameter.
///
/// \code
/// func multiple_trailing_with_defaults(
/// duration: Int,
/// animations: (() -> Void)? = nil,
/// completion: (() -> Void)? = nil) {}
///
/// multiple_trailing_with_defaults(duration: 42) {} // picks `completion:`
/// \endcode
class SpecifyLabelToAssociateTrailingClosure final : public ConstraintFix {
SpecifyLabelToAssociateTrailingClosure(ConstraintSystem &cs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::SpecifyLabelToAssociateTrailingClosure,
locator, /*isWarning=*/true) {}
public:
std::string getName() const override {
return "specify a label to associate trailing closure with parameter";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static SpecifyLabelToAssociateTrailingClosure *
create(ConstraintSystem &cs, ConstraintLocator *locator);
};
/// Diagnose situations where we have a key path with no components.
///
/// \code
/// let _ : KeyPath<A, B> = \A
/// \endcode
class AllowKeyPathWithoutComponents final : public ConstraintFix {
AllowKeyPathWithoutComponents(ConstraintSystem &cs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowKeyPathWithoutComponents, locator) {}
public:
std::string getName() const override { return "key path missing component"; }
bool diagnose(const Solution &solution, bool asNote = false) const override;
static AllowKeyPathWithoutComponents *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
class IgnoreInvalidResultBuilderBody : public ConstraintFix {
protected:
enum class ErrorInPhase {
PreCheck,
ConstraintGeneration,
};
ErrorInPhase Phase;
IgnoreInvalidResultBuilderBody(ConstraintSystem &cs, ErrorInPhase phase,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::IgnoreInvalidResultBuilderBody, locator),
Phase(phase) {}
public:
std::string getName() const override {
return "ignore invalid result builder body";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}
static IgnoreInvalidResultBuilderBody *
duringPreCheck(ConstraintSystem &cs, ConstraintLocator *locator) {
return create(cs, ErrorInPhase::PreCheck, locator);
}
static IgnoreInvalidResultBuilderBody *
duringConstraintGeneration(ConstraintSystem &cs, ConstraintLocator *locator) {
return create(cs, ErrorInPhase::ConstraintGeneration, locator);
}
private:
static IgnoreInvalidResultBuilderBody *
create(ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator);
};
class IgnoreResultBuilderWithReturnStmts final
: public IgnoreInvalidResultBuilderBody {
Type BuilderType;
IgnoreResultBuilderWithReturnStmts(ConstraintSystem &cs, Type builderTy,
ConstraintLocator *locator)
: IgnoreInvalidResultBuilderBody(cs, ErrorInPhase::PreCheck, locator),
BuilderType(builderTy) {}
public:
bool diagnose(const Solution &solution, bool asNote = false) const override;
static IgnoreResultBuilderWithReturnStmts *
create(ConstraintSystem &cs, Type builderTy, ConstraintLocator *locator);
};
class SpecifyContextualTypeForNil final : public ConstraintFix {
SpecifyContextualTypeForNil(ConstraintSystem &cs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::SpecifyContextualTypeForNil, locator) {}
public:
std::string getName() const override {
return "specify contextual type to resolve `nil` literal";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}
static SpecifyContextualTypeForNil *create(ConstraintSystem & cs,
ConstraintLocator * locator);
};
class AllowRefToInvalidDecl final : public ConstraintFix {
AllowRefToInvalidDecl(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowRefToInvalidDecl, locator) {}
public:
std::string getName() const override {
return "ignore invalid declaration reference";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}
static AllowRefToInvalidDecl *create(ConstraintSystem &cs,
ConstraintLocator *locator);
};
/// Diagnose if the base type is optional, we're referring to a nominal
/// type member via the dot syntax and the member name matches
/// Optional<T>.{member} or a .none member inferred as non-optional static
/// member e.g. let _ : Foo? = .none where Foo has a static member none.
class SpecifyBaseTypeForOptionalUnresolvedMember final : public ConstraintFix {
SpecifyBaseTypeForOptionalUnresolvedMember(ConstraintSystem &cs,
DeclNameRef memberName,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::SpecifyBaseTypeForOptionalUnresolvedMember,
locator, /*isWarning=*/true),
MemberName(memberName) {}
DeclNameRef MemberName;
public:
std::string getName() const override {
const auto name = MemberName.getBaseName();
return "specify unresolved member optional base type explicitly '" +
name.userFacingName().str() + "'";
}
bool diagnose(const Solution &solution, bool asNote = false) const override;
static SpecifyBaseTypeForOptionalUnresolvedMember *
attempt(ConstraintSystem &cs, ConstraintKind kind, Type baseTy,
DeclNameRef memberName, FunctionRefKind functionRefKind,
MemberLookupResult result, ConstraintLocator *locator);
};
} // end namespace constraints
} // end namespace swift
namespace llvm {
template <>
struct DenseMapInfo<swift::constraints::FixKind> {
using FixKind = swift::constraints::FixKind;
static inline FixKind getEmptyKey() {
return static_cast<FixKind>(0);
}
static inline FixKind getTombstoneKey() {
return static_cast<FixKind>(1);
}
static unsigned getHashValue(FixKind kind) {
return static_cast<unsigned>(kind);
}
static bool isEqual(FixKind lhs, FixKind rhs) {
return lhs == rhs;
}
};
}
#endif // SWIFT_SEMA_CSFIX_H