mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
1681 lines
59 KiB
C++
1681 lines
59 KiB
C++
//===--- 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/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 "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;
|
|
|
|
/// 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 function builder.
|
|
SkipUnhandledConstructInFunctionBuilder,
|
|
|
|
/// 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 representative accessible
|
|
/// using `.rawValue` member.
|
|
UseValueTypeOfRawRepresentative,
|
|
/// 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,
|
|
|
|
AllowInvalidUseOfTrailingClosure,
|
|
|
|
/// 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,
|
|
|
|
/// 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 infered because the module where
|
|
/// the default type that implements the associated literal protocol
|
|
/// is declared was not imported.
|
|
SpecifyObjectLiteralTypeImport,
|
|
|
|
};
|
|
|
|
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(ArrayRef<ConstraintFix *> secondaryFixes,
|
|
bool asNote = false) const {
|
|
return diagnose(asNote);
|
|
}
|
|
|
|
/// Diagnose a failure associated with this fix.
|
|
virtual bool diagnose(bool asNote = false) const = 0;
|
|
|
|
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.
|
|
Expr *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;
|
|
|
|
UnwrapOptionalBase(ConstraintSystem &cs, FixKind kind, DeclNameRef member,
|
|
ConstraintLocator *locator)
|
|
: ConstraintFix(cs, kind, locator), MemberName(member) {
|
|
assert(kind == FixKind::UnwrapOptionalBase ||
|
|
kind == FixKind::UnwrapOptionalBaseWithOptionalResult);
|
|
}
|
|
|
|
public:
|
|
std::string getName() const override {
|
|
return "unwrap optional base of member lookup";
|
|
}
|
|
|
|
bool diagnose(bool asNote = false) const override;
|
|
|
|
static UnwrapOptionalBase *create(ConstraintSystem &cs, DeclNameRef member,
|
|
ConstraintLocator *locator);
|
|
|
|
static UnwrapOptionalBase *
|
|
createWithOptionalResult(ConstraintSystem &cs, DeclNameRef member,
|
|
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(bool asNote = false) const override;
|
|
|
|
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(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(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(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(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(bool asNote = false) const override;
|
|
|
|
static ContextualMismatch *create(ConstraintSystem &cs, Type lhs, Type rhs,
|
|
ConstraintLocator *locator);
|
|
};
|
|
|
|
/// 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(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(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->throws() != toType->throws());
|
|
}
|
|
|
|
public:
|
|
std::string getName() const override { return "drop 'throws' attribute"; }
|
|
|
|
bool diagnose(bool asNote = false) const override;
|
|
|
|
static DropThrowsAttribute *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(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(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(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(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(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(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(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(bool asNote = false) const override;
|
|
|
|
static InsertExplicitCall *create(ConstraintSystem &cs,
|
|
ConstraintLocator *locator);
|
|
};
|
|
|
|
class UsePropertyWrapper final : public ConstraintFix {
|
|
VarDecl *Wrapped;
|
|
bool UsingStorageWrapper;
|
|
Type Base;
|
|
Type Wrapper;
|
|
|
|
UsePropertyWrapper(ConstraintSystem &cs, VarDecl *wrapped,
|
|
bool usingStorageWrapper, Type base, Type wrapper,
|
|
ConstraintLocator *locator)
|
|
: ConstraintFix(cs, FixKind::UsePropertyWrapper, locator),
|
|
Wrapped(wrapped), UsingStorageWrapper(usingStorageWrapper), Base(base),
|
|
Wrapper(wrapper) {}
|
|
|
|
public:
|
|
std::string getName() const override {
|
|
return "insert '$' or '_' to use property wrapper type instead of wrapped type";
|
|
}
|
|
|
|
bool diagnose(bool asNote = false) const override;
|
|
|
|
static UsePropertyWrapper *create(ConstraintSystem &cs, VarDecl *wrapped,
|
|
bool usingStorageWrapper, 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 usingStorageWrapper() 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(bool asNote = false) const override;
|
|
|
|
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(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(bool asNote = false) const override;
|
|
|
|
static DefineMemberBasedOnUse *create(ConstraintSystem &cs, Type baseType,
|
|
DeclNameRef member, bool alreadyDiagnosed,
|
|
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(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(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(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(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(ArrayRef<ConstraintFix *> secondaryFixes,
|
|
bool asNote = false) const override;
|
|
|
|
bool diagnose(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(ArrayRef<ConstraintFix *> secondaryFixes,
|
|
bool asNote = false) const override;
|
|
|
|
bool diagnose(bool asNote = false) 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(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(bool asNote = false) const override;
|
|
|
|
static AllowClosureParamDestructuring *create(ConstraintSystem &cs,
|
|
FunctionType *contextualType,
|
|
ConstraintLocator *locator);
|
|
};
|
|
|
|
class AddMissingArguments final
|
|
: public ConstraintFix,
|
|
private llvm::TrailingObjects<AddMissingArguments,
|
|
AnyFunctionType::Param> {
|
|
friend TrailingObjects;
|
|
|
|
using Param = AnyFunctionType::Param;
|
|
|
|
unsigned NumSynthesized;
|
|
|
|
AddMissingArguments(ConstraintSystem &cs,
|
|
llvm::ArrayRef<Param> 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<Param> getSynthesizedArguments() const {
|
|
return {getTrailingObjects<Param>(), NumSynthesized};
|
|
}
|
|
|
|
bool diagnose(bool asNote = false) const override;
|
|
|
|
static AddMissingArguments *create(ConstraintSystem &cs,
|
|
llvm::ArrayRef<Param> synthesizedArgs,
|
|
ConstraintLocator *locator);
|
|
|
|
private:
|
|
MutableArrayRef<Param> getSynthesizedArgumentsBuf() {
|
|
return {getTrailingObjects<Param>(), 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(bool asNote = false) const override;
|
|
|
|
/// 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(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(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(bool asNote = false) const override;
|
|
|
|
static AllowAnyObjectKeyPathRoot *create(ConstraintSystem &cs,
|
|
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(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,
|
|
} 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";
|
|
}
|
|
llvm_unreachable("covered switch");
|
|
}
|
|
|
|
bool diagnose(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 ConstraintFix {
|
|
RemoveReturn(ConstraintSystem &cs, ConstraintLocator *locator)
|
|
: ConstraintFix(cs, FixKind::RemoveReturn, locator) {}
|
|
|
|
public:
|
|
std::string getName() const override { return "remove or omit return type"; }
|
|
|
|
bool diagnose(bool asNote = false) const override;
|
|
|
|
static RemoveReturn *create(ConstraintSystem &cs, 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(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(ArrayRef<ConstraintFix *> secondaryFixes,
|
|
bool asNote = false) const override;
|
|
|
|
bool diagnose(bool asNote = false) const override;
|
|
|
|
static DefaultGenericArgument *create(ConstraintSystem &cs,
|
|
GenericTypeParamType *param,
|
|
ConstraintLocator *locator);
|
|
};
|
|
|
|
class SkipUnhandledConstructInFunctionBuilder final : public ConstraintFix {
|
|
public:
|
|
using UnhandledNode = llvm::PointerUnion<Stmt *, Decl *>;
|
|
|
|
private:
|
|
UnhandledNode unhandled;
|
|
NominalTypeDecl *builder;
|
|
|
|
SkipUnhandledConstructInFunctionBuilder(ConstraintSystem &cs,
|
|
UnhandledNode unhandled,
|
|
NominalTypeDecl *builder,
|
|
ConstraintLocator *locator)
|
|
: ConstraintFix(cs, FixKind::SkipUnhandledConstructInFunctionBuilder,
|
|
locator),
|
|
unhandled(unhandled), builder(builder) { }
|
|
|
|
public:
|
|
std::string getName() const override {
|
|
return "skip unhandled constructs when applying a function builder";
|
|
}
|
|
|
|
bool diagnose(bool asNote = false) const override;
|
|
|
|
static SkipUnhandledConstructInFunctionBuilder *
|
|
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(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(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(bool asNote = false) 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(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";
|
|
}
|
|
|
|
bool diagnose(bool asNote = false) const override;
|
|
|
|
static AllowArgumentMismatch *create(ConstraintSystem &cs, Type argType,
|
|
Type paramType,
|
|
ConstraintLocator *locator);
|
|
};
|
|
|
|
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(bool asNote = false) const override;
|
|
|
|
static ExpandArrayIntoVarargs *attempt(ConstraintSystem &cs, Type argType,
|
|
Type paramType,
|
|
ConstraintLocatorBuilder locator);
|
|
};
|
|
|
|
class ExplicitlyConstructRawRepresentable final : public AllowArgumentMismatch {
|
|
ExplicitlyConstructRawRepresentable(ConstraintSystem &cs, Type argType,
|
|
Type paramType,
|
|
ConstraintLocator *locator)
|
|
: AllowArgumentMismatch(cs, FixKind::ExplicitlyConstructRawRepresentable,
|
|
argType, paramType, locator) {}
|
|
|
|
public:
|
|
std::string getName() const override {
|
|
return "explicitly construct a raw representable type";
|
|
}
|
|
|
|
static ExplicitlyConstructRawRepresentable *
|
|
attempt(ConstraintSystem &cs, Type argType, Type paramType,
|
|
ConstraintLocatorBuilder locator);
|
|
};
|
|
|
|
class UseValueTypeOfRawRepresentative final : public AllowArgumentMismatch {
|
|
UseValueTypeOfRawRepresentative(ConstraintSystem &cs, Type argType,
|
|
Type paramType, ConstraintLocator *locator)
|
|
: AllowArgumentMismatch(cs, FixKind::UseValueTypeOfRawRepresentative,
|
|
argType, paramType, locator) {}
|
|
|
|
public:
|
|
std::string getName() const override {
|
|
return "use `.rawValue` of a raw representable type";
|
|
}
|
|
|
|
static UseValueTypeOfRawRepresentative *
|
|
attempt(ConstraintSystem &cs, Type argType, Type paramType,
|
|
ConstraintLocatorBuilder locator);
|
|
};
|
|
|
|
/// Replace a coercion ('as') with a forced checked cast ('as!').
|
|
class CoerceToCheckedCast final : public ContextualMismatch {
|
|
CoerceToCheckedCast(ConstraintSystem &cs, Type fromType, Type toType,
|
|
ConstraintLocator *locator)
|
|
: ContextualMismatch(cs, FixKind::CoerceToCheckedCast, fromType, toType,
|
|
locator) {}
|
|
|
|
public:
|
|
std::string getName() const { return "as to as!"; }
|
|
|
|
bool diagnose(bool asNote = false) const;
|
|
|
|
static CoerceToCheckedCast *attempt(ConstraintSystem &cs, Type fromType,
|
|
Type toType, ConstraintLocator *locator);
|
|
};
|
|
|
|
class RemoveInvalidCall final : public ConstraintFix {
|
|
RemoveInvalidCall(ConstraintSystem &cs, ConstraintLocator *locator)
|
|
: ConstraintFix(cs, FixKind::RemoveCall, locator) {}
|
|
|
|
public:
|
|
std::string getName() const {
|
|
return "remove extraneous call from value of non-function type";
|
|
}
|
|
|
|
bool diagnose(bool asNote = false) const;
|
|
|
|
static RemoveInvalidCall *create(ConstraintSystem &cs,
|
|
ConstraintLocator *locator);
|
|
};
|
|
|
|
class AllowInvalidUseOfTrailingClosure final : public AllowArgumentMismatch {
|
|
AllowInvalidUseOfTrailingClosure(ConstraintSystem &cs, Type argType,
|
|
Type paramType, ConstraintLocator *locator)
|
|
: AllowArgumentMismatch(cs, FixKind::AllowInvalidUseOfTrailingClosure,
|
|
argType, paramType, locator) {}
|
|
|
|
public:
|
|
std::string getName() const {
|
|
return "allow invalid use of trailing closure";
|
|
}
|
|
|
|
bool diagnose(bool asNote = false) const;
|
|
|
|
static AllowInvalidUseOfTrailingClosure *create(ConstraintSystem &cs,
|
|
Type argType, Type paramType,
|
|
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(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 {
|
|
const auto baseName = MemberName.getBaseName();
|
|
return "specify base type in reference to member '" +
|
|
baseName.userFacingName().str() + "'";
|
|
}
|
|
|
|
bool diagnose(bool asNote = false) const;
|
|
|
|
static SpecifyBaseTypeForContextualMember *
|
|
create(ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator);
|
|
};
|
|
|
|
class SpecifyClosureReturnType final : public ConstraintFix {
|
|
SpecifyClosureReturnType(ConstraintSystem &cs, ConstraintLocator *locator)
|
|
: ConstraintFix(cs, FixKind::SpecifyClosureReturnType, locator) {}
|
|
|
|
public:
|
|
std::string getName() const {
|
|
return "specify closure return type";
|
|
}
|
|
|
|
bool diagnose(bool asNote = false) const;
|
|
|
|
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 {
|
|
return "import required module to gain access to a default literal type";
|
|
}
|
|
|
|
bool diagnose(bool asNote = false) const;
|
|
|
|
static SpecifyObjectLiteralTypeImport *create(ConstraintSystem &cs,
|
|
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
|